南无阿弥陀佛,愿所有众生得大智慧,离贪嗔痴,得涅槃乐! 窗口: 逻辑环境中的一小部分,是一个矩形框;世界坐标系是逻辑坐标,SetWindowOrg(X,Y )设置窗口的逻辑坐标点(X,Y)映射为的设备环境的设备点(0,0)。 1. 窗口和视口以及设备环境出现的缘起 窗口和视口实际上描述的是同一个客观场景,它们之间的区别仅仅在于两者坐标系的单位不同。设备环境就是显示设备。窗口和视口中的场景必须通过映射模式正确的显示到设备环境中。 默认情况下,Windows GDI 绘图中的窗口坐标原点、视口坐标原点以及设备点重合在一起都是在设备点(0,0)处。Windows GDI的设备环境原点是位于屏幕的左上角,设备点始终位于(0,0)点,x轴向右,y轴向上;OpenGL使用的窗口坐标和WindowsGDI使用的窗口坐标是不一样的。OpengL默认世界窗口坐标原点位于屏幕中心,x轴向右,y轴向上。视口原点在屏幕左下角。 视口与窗口表征的是同一个实际几何物体,所以有窗口必定有视口,有视口必定有窗口。窗口或视口可以理解为用眼睛看到的物体实体例如一个球,设备环境显示区域(显示屏)可以理解为由于计算机屏幕长宽比不同,实际物体在计算机屏幕上看到的最终显示结果,例如显示屏中的球看起来变成一个椭球。几乎在所有的GDI函数中使用的坐标值都是采用的逻辑单位,也就是物体实际多大就是多大。Windows必须将逻辑单位转换为“设备单位”---像素,也就是物体在设备环境(显示屏或打印机)显示后看起来应该多大。这种转换是由映射方式、窗口和视口的原点以及窗口和视口的范围所控制的。 通过不同的映射方式可以在逻辑单位下改变窗口原点的位置而将物体现实在设备环境中,也可以在以像素为单位的视口坐标系下移动视口将物体显示在显示屏上,不管对窗口和视口原点作什么改变,设备点(0,0)始终是客户区的左上角,两者都是为了让物体在显示屏上现实。调整窗口原点和调整视口原点可以达到相同的效果。 视口是与设备相关的一个矩形区域,坐标单位是与设备相关的,直观的视口原点坐标的移动就是dc的移动。窗口的坐标是逻辑坐标,与设备无关。窗口坐标的原点与视口坐标的原点始终对应于同一点。对于同一个图形,用窗口坐标系统表达的该区域的长和宽与视口的坐标系统表达的长和宽是不同的,因单位不同。二者就定义了这两个坐标系统的比例关系。程序作图时,使用的坐标总是是窗口坐标,而实际的显示或输出设备却各有自己的坐标。例如,有的打印机设备水平和垂直分辨率不同,其象素实际上是长方形。程序编写画一个圆,若不经任何坐标转换,在打印机上输出的就是个椭圆。 2. 窗口和视口理解要点 窗口与视口一向是初学者比较难以理解的难点,本人以前也是糊里糊涂的,不过最近有时间去深入研究之后,才彻底弄明白,摆脱了以前很多错误的观念。弄清楚了这些才会更好的使用不同的坐标影射模式,更灵活的为自己的绘图带来便利: 首先要清楚窗口和视口的坐标原点始终是同一个点,窗口和视口中的内容是同样的内容。设备坐标(显示器)中则会根据视口或窗口原点的改变而显示出不同的内容。在MM_TEXT映射模式下世界坐标系第三象限的内容就是窗口坐标系下第一象限的内容,所以有SetViewportOrg(x, y)与SetWindowOrg(-x,-y)具有相同的效果。这个很难理解但是这是事实。“设备环境”如显示屏始终是只有左手坐标系xoy第一象限的区域。开始时窗口坐标原点、视口坐标原点以及设备点重合在一起都是在客户区的左上角为了将物体显示在现实屏上,通过SetWindowOrg(x,y)将"画布"(世界坐标系原点)向屏幕的左边移动x个单位,向屏幕的上方移动y个单位。SetViewportOrg(x,y)是将"画笔"(视口原点)向屏幕的右边移动x个单位,向屏幕的下方移动y个单位。 现将其关键点归纳如下: 1、 视口与显示设备有关,视口等同客户区,使用设备坐标。视口是和窗口等同的一块矩形区域,它的x轴向右和y轴向下。 2、 窗口与显示设备无关,窗口与视口为同一区域,但使用逻辑坐标,它的x轴向右,y轴向上。 3、 窗口与视口使用不同的坐标系,但是两套坐标系的原点始终为同一点。但该点坐标(不管是视口坐标原点还是窗口坐标原点)不一定为(0,0)。窗口就是视口,去吧仅仅在于两者建立的坐标系不同,移动窗口的同时就是移动视口,反之亦然。 It's easy to get SetViewportOrg and SetWindowOrg confused, but the distinction between them is actually quite clear. Changing the viewport origin to (x,y) with SetViewportOrg tells Windows to map the logical point (0,0) to the device point (x,y). Changing the window origin to (x,y) with SetWindowOrg does essentially the reverse, telling Windows to map the logical point (x,y) to the device point (0,0)—the upper left corner of the display surface. In the MM_TEXT mapping mode, the only real difference between the two functions is the signs of x and y. In other mapping modes, there's more to it than that because SetViewportOrg deals in device coordinates and SetWindowOrg deals in logical coordinates. <<Programing windows with MFC>>中这样来描述 dc始终开始从世界坐标系的原点开始画,画笔的原点就是画布的原点就是世界坐标系的原点。在MM_TEXT下通过SetViewportOrg(X,Y)和SetWindowOrg(-X,-Y)将世界坐标系的原点移动到设备环境坐标系下的(X,Y)点。SetWindowOrg(-X,-Y)指设备点(0,0)处对应dc绘图的逻辑点(-X,-Y),逻辑点(0,0)在MM_TEXT映射模式下位于设备坐标系的(X,Y)点。 1) 移动视口原点好比移动画笔dc,如果将视口原点设置为(xViewOrg,yViewOrg),相当于dc画笔的移动,则逻辑点(0,0)就会被映射为设备点(xViewOrg,yViewOrg),初始dc绘图在逻辑点(0,0)下笔,图形将以客户区(设备点)的(xViewOrg,yViewOrg)为中心进行绘制。在MM_TEXT映射模式下SetViewportOrg(150,100);dc绘图坐标原点从(0,0)向右下方移动到(150,100)。可以看出SetViewportOrg()函数可以更改设备上下文dc的坐标原点。 void CDemoView::OnPaint() 2) 移动窗口原点好比画布的移动,如果将窗口原点改变为(xWinOrg,yWinOrg),则逻辑点(xWinOrg,yWinOrg)将会被映射为设备点(0,0),SetWindowOrg(150,100);逻辑点(150,100)对应于设备点(0,0);不管对窗口和视口原点作什么改变,应该铭记第一设备点(0,0)始终是客户区的左上角,第二窗口和视口原点是同一个点。 void CDemoView::OnPaint() 为了能够在计算机中更好的描述真实的世界,必须设定一种与真实世界相符的逻辑坐标系统。当然Windows为我们提供了设备坐标与逻辑坐标进行映射的接口。 Windows提供的坐标系统映射的接口有: 3.孙鑫老师关于图形错位的说明 当我们在窗口中点击鼠标左键的时候,得到的是设备坐标(680,390),在MM_TEXT的映射模式下,逻辑坐标和设备坐标是相等的,所以我们利用集合类保存的这个点的坐标是以象素为单位,坐标值为(680,390)。在调用OnDraw函数前,在OnPaint函数中调用了OnPrepareDC函数,OnPrepareDC函数内调用SetViewportOrgEx ()调整了显示上下文的属性,将视口的原点设置为了(0,-150),这样的话,窗口的原点,也就是逻辑坐标(0,0)将被映射为设备坐标(0,-150),在画线的时候,因为GDI的函数使用的是逻辑坐标,而图形在显示的时候,Windows需要将逻辑坐标转化为设备坐标,因此,原先保存的坐标点(680,390)(在GDI函数中,作为逻辑坐标使用),根据转换公式 xViewport = xWindow-xWinOrg+xViewOrg 和yViewport = yWindow-yWinOrg+yViewOrg, 得到设备点的x坐标为680-0+0=680,设备点的y坐标为390-0+(-150)=240,于是我们看到图形在原先显示地方的上方出现了。 关于解决方法的说明 首先我们在绘制图形之后,在保存坐标点之前,调用OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为(0,-150),这样的话,窗口的原点,也就是逻辑坐标(0,0)将被映射为设备坐标(0,-150),然后我们调用DPtoLP函数将设备坐标(680,390)转换为逻辑坐标,根据设备坐标转换为逻辑坐标的公式:
从Windows的鼠标消息可以获得鼠标指针的当前坐标值(point.x和point.y)此坐标值是设备坐标。 CDC的LPtoDP函数可以将逻辑坐标转换成设备坐标 下面列出我们应该在什么时候使用什么样的坐标系一定要记住: ◎CDC的所有成员函数都以逻辑坐标为参数 |
|