GDI中的坐标映射问题(3)

3.从程序员的角度来看坐标映射
   坐标映射在程序员的眼中就是要根据自己实际问题的要求,构造出一个满足要求的逻辑空间。所谓的满足要求就是指每一个我们在程序中使用的点,都能出现在physical device上我们预期的相应位置。由于device空间到physical device空间是一对一的映射,因此,我们完全可以将绘图目的地看成device空间,所构造出的逻辑空间也只需正确映射到device空间就可以了。

3.1 page空间 → device空间

    如果我们不使用world空间,此时的逻辑空间就是page空间。下面来看如何确定它的三个要素:单位刻度值、方向、原点。
首先要使用SetMapMode(int)函数选择映射模式。这其中有6种事先已经定义好了的模式,可以直接拿来就用,比如MM_HIMETRIC模式表示page空间的单位刻度是0.01毫米,x轴正向向右,y轴正向向上,原点与device空间的原点重合。如果此时程序中有一条值为10的线段,那么在程序员的眼中,这就是一条10×0.01=0.1毫米的线段,不管使用多大分辨率的显示器它都是这么长,我们甚至可以用尺子在屏幕上量量试试。如果选择预定义的映射模式,相当于微软已经为我们构造好了page空间,下面的事我们就都不用做了。
但是很多时候,微软的东西不一定适合我们,此时就要将映射模式设定为MM_ISOTROPIC或者MM_ANISOTROPIC,使用下面的四个函数定义我们自己的坐标系:

SetWindowExt(int Lwidth, int Lheight) //参数的单位为逻辑单位(Logical),如果参数为负值表示window相应的坐标轴与page空间相反。
SetViewportExt(int Pwidth, int Pheight) //参数的单位为像素(Pixel),如果参数为负值表示viewport相应的坐标轴与device空间相反。
SetWindowOrg(int Lx, int Ly)。
SetViewportOrg(int Px, int Py)。

这四个函数提出了两个新的概念:window和viewport,它们分别与page空间和device空间对应,但请记住并不是对等。引入它们的目的仅仅是为了确定page空间的单位刻度、方向、原点。

1.x轴的单位刻度=| Pwidth | / | Lwidth |。

   这表示x轴上一个逻辑单位等于多少个像素。下面举例加以解释。
   比如我们先通过GetDeviceCap(LOGPIXELSX)获得在我们的显示器上每英寸等于多少个像素,设为p,然后我们将它赋给Pwidth,将Lwidth赋成2,即Pwidth / Lwidth=p / 2。那么,此时page空间x轴上的单位刻度就是p / 2个像素;又由于p个像素是代表一个英寸的,所以此时的page空间x轴上的单位刻度同时也是半个英寸。
   请注意这个例子中,虽说viewport的x方向“范围”是p个像素,但是device空间x轴的“范围”决不仅仅是p个像素,而是2^27个像素,至于可视的范围到底是多少,则取决于物理设备空间。

2.x轴的方向:这个好确定,Lwidth与Pwidth同号,则page空间的x轴方向与device空间x轴方向相同,否则相反。

3.原点。这个就有一点麻烦了,我们需将window与viewport进行重叠,包括原点和坐标轴方向,然后才可以确定page空间的原点。下面通过一个例子来加以说明。

例:假设我们通过下面的语句构造了一个page空间:

SetMapeMode(MM_ANISOTROPIC);
SetWindowExt(10, 100);
SetWindowOrg(0, -100);
SetViewportExt(20, 200);
SetViewPortOrg(0,-200);

(由于100个逻辑单位相当于200个像素,因此我将它们的示意长度画成一样。)

    从这些语句中我们可以很快确定出page空间的单位刻度(比如y轴上每逻辑单位200 / 100=2个像素),以及y轴的方向与device空间相同(100与200同号),但是page空间的原点在哪里呢?请看:
首先我们分别在page空间中画出window坐标系、在device空间中画出viewport坐标系(如图2的左边部分)。然后由于例子中的window坐标方向与viewport相反,还需将page空间翻转(见图2中间部分)。最后将window与viewport重叠(见图2右边部分),使它们的原点和坐标方向都一致。此时我们可以清楚地看到,page空间的原点就对应于device空间的原点,而且方向也和它相同。
通过以上的1、2、3点我们就可以完全确定一个适合我们自己要求的page空间,当我们不要world空间时,它就是逻辑空间。
另外还有一个问题就是要注意MM_ANISOTROPIC与MM_ISOTROPIC的区别。对于前者来说,x方向的单位刻度与y方向的单位刻度可以不同(当然也可以相同),但是后者x方向的单位刻度与y方向的单位刻度一定是相同的,如果通过计算window与viewport范围的比值得到两个方向的单位刻度值不同,那么将会以较小的那个为准。
3.2 world空间 → page空间
    有时候我们需要从一个倾斜的角度显示一个圆或者其它什么图形,但是我们在使用绘图语句时,心目中仍然要当这个圆正对着我们来考虑问题,因为只有这样,我们在构造图形时的思维才不至于混乱,怎么实现呢?就可以通过加上world空间达到这个目的。由于一般很少使用这种映射,我在这里只以一个例子简单加以说明。

void CSampleView::DrawShearCircle()

{
       CClientDC dc(this);
       dc.SetMapMode(MM_ANISOTROPIC);  //映射模式设定为各向异性。
       //以下语句将page空间最小刻度值设为1mm,原点位于客户区矩形中心,x正向向右,y正向向上。

       dc.SetWindowExt(1, -1);
       int PperMMX = dc.GetDeviceCaps(HORZRES) / dc.GetDeviceCaps(HORZSIZE);
       int PperMMY = dc.GetDeviceCaps(VERTRES) / dc.GetDeviceCaps(VERTSIZE);
       dc.SetViewportExt(PperMMX, PperMMY);
       CRect cr;
       GetClientRect(&cr);
       dc.SetViewportOrg(cr.right/2, cr.bottom/2);
       dc.SetWindowOrg(0, 0);
       //以下语句设置world空间到page空间的映射规则,将会产生一个y轴的剪切。

SetGraphicsMode(dc.GetSafeHdc(), GM_ADVANCED);   //一定要首先打开GM_ADVANCED。

       XFORM xf;
       xf.eM11 = 1.0;
       xf.eM12 = 1.0;  //y轴方向的剪切常量为1.0
       xf.eM21 = 0.0;  //x轴方向的剪切常量为0.0
       xf.eM22 = 1.0;
       xf.eDx  = 0.0;
       xf.eDy  = 0.0;
       SetWorldTransform(dc.GetSafeHdc(), &xf);
       dc.Rectangle(-50, 50, 50, -50);  //这个矩形的中心在客户区中心,长度为100mm。不过由于设置了world空间,尽管从语句上来看是一个正方形,但是实际显示的却是一个锐角为45°的菱形。
       dc.Ellipse(-50, 50, 50, -50);  //尽管从语句上来看是一个圆,但是实际显示的却是一个椭圆。

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值