P155 ~ P156

对于时钟程序,或许想使用如下这样一个四象限笛卡儿坐标系统:四个方向的轴可以任意缩放,并且逻辑点(0,0)位于客户区的中心。如果希望每个轴的范围是 0 到 1000(举个例子),可以使用下面的代码:

SetMapMode            (hdc, MM_ISOTROPIC) ;
SetWindowExtEx      (hdc, 1000, 1000, NULL) ;
SetViewportExtEx     (hdc, cxClient / 2, -cyClient / 2, NULL) ;
SetWindowOrgEx    (hdc, cxClient / 2, cyClient / 2, NULL) ;

错误语句

SetWindowOrgEx    (hdc, cxClient / 2, cyClient / 2, NULL) ;

该条语句实现了逻辑点(cxClient / 2, cyClient / 2)映射到视口原点。

但无法实现将逻辑点(0,0)位于客户区中心,即设备点(cxClient / 2, cyClient / 2)处。

根据 SetViewportExtEx 的参数可知,逻辑坐标轴,X 轴坐标向右增长, Y 轴坐标向上增长。

所以实际上语句执行后逻辑点(0,0)位于设备点(-cxClient / 2, cyClient / 2)处。

无法在客户区进行显示。

这里需要知道,设备坐标点(0,0)始终在客户区的左上角,且设备点始终右移 X 值增加,下移 Y 值增加。

英文版中使用的语句是

SetViewportOrgEx    (hdc, cxClient / 2, cyClient / 2, NULL) ;

该语句实现将逻辑点(0,0)映射到设备点(cxClient / 2, cyClient / 2)上。

即客户区中心。



 

估计这应当是一个抄写错误。

如若需要使用 SetWindowOrgEx 函数实现,代码应为

SetMapMode (hdc, MM_ISOTROPIC) ; 
SetWindowExtEx (hdc,
1000, 1000, NULL) ;
SetViewportExtEx (hdc, cxClient
, -cyClient, NULL) ;
SetWindowOrgEx(hdc, -500, 500, NULL) ;

该语句实现了将逻辑点(-500,500)映射到视口原点。

因为窗口范围被设定为了(1000,1000)。

所以逻辑点(0,0)将被映射到客户区中心。

但因为 MM_ISOTROPIC 模式下,Windows 会对逻辑窗口进行调节,以使逻辑单位在两个坐标轴上表示相同的物理尺寸。

所以实际上逻辑点(0,0)被映射到了由 Windows 调整后的,一个正方形的逻辑窗口的中心。

正方形的边长等于客户区的长或宽中较小的一个,左上角顶点为视口顶点。

当客户区为正方形时则逻辑点(0,0)将被映射到客户区中心。



如需实现客户区窗口形状对执行结果无影响,则需要相应调整视口原点。

下面的代码实现了与英文原版代码相同的功能

#define    GT(a,b)    a > b ? (a - b) / 2 : 0
SetMapMode(hdc, MM_ISOTROPIC) ;
SetWindowExtEx(hdc, 1000, 1000, NULL) ;
SetViewportExtEx(hdc, cxClient, -cyClient, NULL) ;
SetWindowOrgEx(hdc, -500, 500, NULL) ;
SetViewportExtEx(hdc, GT(cxClient,cyClient), GT(cyClient,cxClient), NULL) ;