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) ;