本篇学习自:孔令德老师的《三维计算机图形学》
这里定义的项目名称应当为英文,且工程会自动创建对应名称的头文件(.h)以及源文件(.cpp)。
项目文件会创建在自己定义的路径位置下。
单击创建。
这里选择单文档,是因为我们只需要一个交互式窗口,完成对应的绘图任务,不必使用复杂项目工程的多文档窗口。
使用MFC标准,点击完成。
在解决方案中,包含了新建工程的所有文件,单击可查看对应文件的代码。
其中,各类的功能简述如下:
CAboutDlg类:帮助类,用于说明这个工程的开发信息
CMainFrame类:主框架类
CnameApp类:应用程序入口
其中“C”代表class,“类别”的意思。
“name”是在1.3中我们自定义的项目工程名。
“App”代表application应用程序。
CnameDoc类:文档类,用来管理,存放数据
“Doc”代表document文档。
CnameView类:将文档中的数据可视化,绘制图形就是通过该类实现。
上方窗口显示我们已经定义的类。
选中某个类别后,会在下方窗口显示该类的成员函数。
其中Text其实就是我们的项目工程名,例如刚刚创建的工程name。
我们进行绘图的代码需要写在CnameView类中的OnDraw函数里。
第一步都是将/*pDC*/
替换成pDC
(被注释的指针要放开),便于后文绘图使用。
这里对指针pDC举一个简单的例子,画一条直线。
在// TODO: 在此处为本机数据添加绘制代码
之后添加代码:
pDC->MoveTo(100,100); // 起点
pDC->LineTo(400,400); // 与上一个点连线连线
pDC->LineTo(200,400);
pDC->LineTo(100,100);
默认坐标系仅有一个象限(图左),但是平时生活中使用的都是具备四个象限的坐标系(图右),这样横纵坐标可以是负值。
自定义的坐标系选择最后一个,由于是宏定义,参数写成MM_ANISOTROPIC
和8
是等价的。
自定义坐标系,将原定选定在客户区中心,x轴水平向右,y轴竖直向上。
清除之前添加的代码。
在CnameView类中的OnDraw函数里添加代码:
CRect rect; // 矩形对象(含有宽高等)
GetClientRect(rect); // 初始化为视区大小
pDC->SetMapMode(8); // 选择映射模式为自定义
pDC->SetWindowExt(rect.Width(), rect.Height()); // 设置窗口大小
pDC->SetViewportExt(rect.Width(), -rect.Height()); // 设置视区大小
pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2); // 设置视区原点位置
rect.OffsetRect(-rect.Width()/2, -rect.Height()/2); //设置偏移函数,对正客户区
讲解:
virtual int SetMapMode(int nMapMode);
形参用于指定新的映射模式,这里自定义取值为8。virtual CSize SetWindowExt(int cx, int cy);
virtual CSize SetViewportExt(int cx, int cy);
virtual CPoint SetViewportOrg(int x, int y);
可以简单画出坐标轴(当时运行试了一下,然后我就删了这段代码):
pDC->MoveTo(-rect.Width() / 2, 0); // 起点
pDC->LineTo(rect.Width() / 2, 0); // 与上一个点连线连线
pDC->MoveTo(0, -rect.Height() / 2);
pDC->LineTo(0, rect.Height() / 2);
在自定义坐标系之后添加测试代码:
CPoint p0(-200,-100), p1(200,-100), p2 (0,200); // 三个顶点坐标
// 一个简单的三角形
pDC->MoveTo(p0); // 从p0出发
pDC->LineTo(p1);
pDC->LineTo(p2);
pDC->LineTo(p0); // 到p0终止
CDC类
第一个C代表class类,DC代表设备上下文,定义了设备上下文对象的基类,封装了绘图所需的成员函数。
GDI是应用程序的图形设备接口,抽象之后得到CDC类。
我们在绘图时,经常使用到该类型的指针pDC
,但是有一个限制,不能同时使用超过五个设备上下文CDC对象。
绘图工具类
CGdiObject
属于Windows GDI绘图工具的基类,其派生类包含位图CBitmap
, 画刷CBrush
, 字体CFont
, 调色板CPalette
, 画笔CPen
。小知识
如何快速得到随机数?
使用如下的语句
srand((unsigned)time(NULL)); //时间函数得到真随机
rand() % 255 //随机数的范围为 0~254
RGB宏红色的两种表示方式的差异是?
下面的绘制像素点函数SetPixel,这个函数会返回RGB值,或者使用仅仅返回bool值的更加快捷的SetPixelV函数。
CDC::SetPixel //类属
COLORREF SetPixel(int x, int y, COLORREF crColor);
COLORREF SetPixel(POINT point, COLORREF crColor);
COLORREF SetPixelV(int x, int y, COLORREF crColor);
COLORREF SetPixelV(POINT point, COLORREF crColor);
清除之前添加的代码,保留3.3中 坐标系的变换代码。
在CnameView类中的OnDraw函数里添加代码:
srand((unsigned)time(NULL)); // 真随机
COLORREF crColor; // 颜色
//第一四象限随机色像素点构成的正方形
for(int x = 50; x < 350; x++)
for(int y = -50; y < 250; y++)
pDC->SetPixelV(x, y, RGB(rand() % 255,rand() % 255, rand() % 255 ));
//第二三象限随机色像素点构成的正方形
for(int x = 50; x < 350; x++)
for(int y = -50; y < 250; y++)
{
crColor = pDC->GetPixel(x, y); // 这个比较费时
pDC->SetPixelV(-x, y,crColor );
}
使用GDI图形设备接口的步骤一般为:
清除之前手动添加的所有代码。
在CnameView类中的OnDraw函数里添加代码:
注释很详细,力求弄懂设备上下文绘图:
代码
// 定义三个点
CPoint p0(100, 100),p1(300, 200), p2(500, 150);
//定义两个颜色画笔,以及一个保存指针
CPen greenPen, bluePen, *pOldPen;
//创建绿画笔,这里第一个参数见下文“CPen画笔样式”。
greenPen.CreatePen(0, 3,RGB(0,255,0)); //(实线,宽度为1,颜色为RGB(0,255,0))
//CDC类型的指针pDC,就是一个指向GDI对象的工具
//创建新的GDI对象:pDC->SelectObject(&greenPen)
//SelectObject(&greenPen) 会返回 【原GDI对象的指针】
//使用pOldPen 保存 【原GDI对象的指针】
pOldPen = pDC->SelectObject(&greenPen);
//绘制直线(用新的GDI对象绘图)
pDC->MoveTo(p0);
pDC->LineTo(p1);
//恢复设备上下文,到此为止,绿色直线段完成绘制
pDC->SelectObject(pOldPen);
//同理,红色直线段绘制
bluePen.CreatePen(0, 3,RGB(0,0,255)); //(实线,宽度为1,颜色为RGB(0,255,0))
pOldPen = pDC->SelectObject(&bluePen);
pDC->LineTo(p2);
pDC->SelectObject(pOldPen);