这一Part,在几年前看windows程序设计时一直不怎么明白。今天,想开发个游戏,重新拾起这块内容,温故而知新。以下内容参考网上、书上结合自己的理解挖掘而成。
默认情况下,程序都是相对于显示区域的左上角,以图素为单位绘图的。这是内定情况,但不是唯一选择。我们可以用映像方式来改变它。
映像方式
CDC::SetMapMode
virtual intSetMapMode( intnMapMode);
函数功能描述:该函数设置指定设备环境的映射方式,映射方式定义了将逻辑单位转换为设备单位的度量单位,并定义了设备的X、Y轴的方向。
nMapMode:指定新的映射方式,此参数可以是下面列出的任何一个值。
MM_ANISOTROPIC:逻辑单位转换成具有任意比例轴的任意单位,用SetWindowExtEx和SetViewportExtEx函数可指定单位、方向和比例。
MM_HIENGLISH:每个逻辑单位转换为0.001英寸,X的正方面向右,Y的正方向向上。
MM_HIMETRIC:每个逻辑单位转换为0.01毫米,X正方向向右,Y的正方向向上。
MM_ISOTROPIC:逻辑单位转换成具有均等比例轴的任意单位,即沿X轴的一个单位等于沿Y轴的一个单位,用和函数可以指定该轴的单位和方向。图形设备界面(GDI)需要进行调整,以保证X和Y的单位保持相同大小(当设置窗口范围时,视口将被调整以达到单位大小相同)。
MM_LOENGLISH:每个逻辑单位转换为0.1英寸,X正方向向右,Y正方向向上。
MM_LOMETRIC:每个逻辑单位转换为0.1毫米,X正方向向右,Y正方向向上。
MM_TEXT:每个逻辑单位转换为一个图素,X正方向向右,Y正方向向下。默认情况。
MM_TWIPS;每个逻辑单位转换为打印点的1/20(即1/1400英寸),X正方向向右,Y方向向上。
备注:
MM_TEXT方式允许应用程序以设备像素为单位来工作,像素的大小根据设备不同而不同。MM_TEXT与设备比较贴合。。MM_HIENLISH,MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC和MM_TWIPS方式对必须用物理意义单位(如英寸或毫米)制图的应用程序是非常有用的。MM_ISOTROPIC方式保证了1:1的纵横比。MM_HIENLISH方式允许对X和Y坐标分别进行调整。
视端口和窗口
当初只要是这两个在弄混着我。
所谓视端口,我们理解为物理输出的设备,通常是屏幕。设备坐标始终是往右是x轴的正方向,往下是y轴的正方向,单位是像素,这是固定不变的。
而窗口,是逻辑上的。GDI绘图使用的都是逻辑上的坐标。窗口的xy轴方向以及单位大小会根据映像模式以及相关的映射函数来决定。
原点
这里先来看两个函数,
SetWindowOrg(x, y) 是把设备坐标的原点(视口)映射到逻辑坐标的(X, Y)处
SetViewportOrg(x, y) 是把逻辑坐标的原点(窗口)映射到设备坐标的(X, Y)处
这两个函数怎么理解呢,我绘了一张图。
例如执行下面命令后将如图所示:
SetWindowOrg(100, 100);
PS:黑色的代表设备视端口,红色的代表窗口。
也就是说,它们的原点会重合。
上面有说过,GDI绘图使用的是逻辑坐标,也就是窗口的坐标。
假如这里有(500,600),其实映射到设备上时是(400,500),因为他们的原点不在同一处,而坐标单位一样。注意,一般我们观察到的是设备上的东西。
其实,说白了也就是坐标转换,数学里头也有,只是书里说得有点云里雾里的,不够清楚。
有了以上的解释,相信大家应该能明白为什么下面的代码的现实结果会是这样的了吧。
void CMapmodeDlg::OnOK()
{
// TODO: Add extra validation here
CDC *pDC = GetDC(); //获取客户区域的设备上下文
pDC->SetMapMode(MM_LOMETRIC);
pDC->SetWindowOrg(100, 100);
pDC->Rectangle(0, 0, 200, 200);
pDC->SetViewportOrg(100, 100);
pDC->SelectStockObject(GRAY_BRUSH);
pDC->Rectangle(0, 0, 200, 200);
}
坐标单位
无疑,这里的都是正交坐标系,所以不考虑方向了。现在就剩下一个元素,坐标的单位怎么转换。
这里我们有两个函数:
SetViewportExt 设置视端口(物理)的坐标单位。
SetWindowExt 设置窗口(逻辑)的坐标单位。
设置好各自的单位后,两个坐标系将按等比关系转换。当然这主要是应用于MM_ISOTROPIC以及MM_ANISOTROPIC。因为其他的单位都已经有所定义。这里还要注意下MM_ISOTROPIC的XY轴单位是一致的。
另外一个小技巧,当两个坐标系的单位一正一负时,转换过来的方向就会调转过来。效果见下续的例子。
====================================================
建立一个合适的坐标系
建立一个合适的坐标系可以为我们的绘图带来很大的方便。下面介绍一下如何在VC中建立我们想要的坐标系。
一、设备坐标和逻辑坐标
设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。
逻辑坐标(Logical Coordinate)是系统用作记录的坐标。在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:
1. 窗口为非滚动窗口
2. 窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。
在VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻辑坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用 CDC 函数DptoLP()将设备坐标转化为逻辑坐标,同样可以用LptoDP()将逻辑坐标转化为设备坐标。
二、坐标模式
为了在不同的领域使用逻辑坐标,Windows提供了以下8种坐标模式:
分别为MM_TEXT、MM_HIENGLISH、MM_LOENGLISH、MM_HIMETRIC、MM_LOMETRIC、MM_TWIPS、MM_ANISOTROPIC和MM_ISOTROPIC。
三、实例解析
(一) 建立以左上角为原点, X 轴和 Y 轴为 1000 的坐标,如下图我们可以用以下代码:
void CTtView::OnDraw(CDC* pDC)
{
CTtDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetViewportOrg(0,0);
pDC->SetViewportExt(rect.right,rect.bottom);
pDC->SetWindowOrg(0,0);
pDC->SetWindowExt(1000,1000);
pDC->MoveTo(50,50);
pDC->LineTo(50,950);
pDC->LineTo(950,950);
pDC->LineTo(50,50);
}
代码分析:
1. GetClientRect(&rect); 取得客户区矩形区域,将其存放在rect中
2. 用pDC->SetMapMode(MM_ANISOTROPIC);设置映射模式
3. 通过pDC->SetViewportOrg(0,0);设置逻辑坐标的原点。
4. 通过pDC->SetViewportExt(rect.right,rect.bottom);和
pDC->SetWindowExt(1000,1000);来确定逻辑坐标下和设备坐标下的尺寸对应关系
5. 在MM_ANISOTROPIC模式下,X轴单位和Y轴单位可以不相同
6. 坐标方向的确定方法是如果逻辑窗范围和视口范围符号相同,则逻辑坐标的方向和视口的方向相同,即X轴向右为正,Y轴向下为正。
7. 如果将显示模式改为MM_ISOTROPIC,那么X轴单位和Y轴单位一定相同,感兴趣的读者可以自己使一下。
(二)建立以视窗中心为原点的坐标,如下:
用如下代码:
void CTtView::OnDraw(CDC* pDC)
{
CTtDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetViewportOrg(rect.right/2,rect.bottom/2);
pDC->SetViewportExt(rect.right,rect.bottom);
pDC->SetWindowOrg(0,0);
pDC->SetWindowExt(1000,-1000);
pDC->MoveTo(150,150);
pDC->LineTo(-150,-200);
pDC->LineTo(150,-150);
pDC->LineTo(150,150);
}
代码分析:
1. 用 pDC->SetViewportOrg(rect.right/2,rect.bottom/2); 设置视口的原点。
2. 用pDC->SetViewportExt(rect.right,rect.bottom);和pDC->SetWindowExt(1000,-1000);来确定设备坐标和逻辑坐标的单位对应关系。
3. 因为逻辑窗范围和视口范围的符号不一致,纵坐标取反,所以Y轴向上为正。
MM_LOENGLISH、MM_HIENGLISH、MM_LOMETRIC、MM_HIMETRIC、MM_TWIPS这一组是Windows提供的重要的固定比例映射模式。
它们都是x值向右方向递增,y值向下递减,并且无法改变。它们之间的区别在于比例因子见下:
MM_LOENGLISH 0.01英寸
MM_HIENGLISH 0.001英寸
MM_LOMETRIC 0.1mm
MM_HIMETRIC 0.01mm
MM_TWIPS 1/1440英寸 //应用于打印机,一个twip相当于1/20磅,一磅又相当于1/72英寸。