孙鑫vc学习笔记_第11课_part3

来源:互联网 发布:ueba 网络事件 编辑:程序博客网 时间:2024/06/05 04:04
 
#让窗口具有滚动的能力00:44
#在头文件和源文件中手动把view的基类改为CScrollView
#对CScrollView的尺寸进行设置
SetScrollSizes
×映射的模式和映射的方式
     坐标空间
Microsoft Windows下的程序运用坐标空间和转换来对图形输出进行缩放、旋转、平移、斜切和反射。
Coordinate Spaces and Transformations
Msdn:……
一个坐标空间是一个平面的空间,通过使用两个相互垂直并且长度相等的轴来定位二维对象。(x和y轴……)
 
Win32应用程序设计接口(API)使用四种坐标空间:世界坐标系空间、页面空间、设备空间、和物理设备空间。应用程序运用世界坐标系空间对图形输出进行旋转、斜切或者反射。
Win32 API把世界坐标系空间和页面空间称为逻辑空间;最后一种坐标空间(即物理设备空间)通常指应用程序窗口的客户区;但是它也包括整个桌面、完整的窗口(包括框架、标题栏和菜单栏)或打印机的一页或绘图仪的一页纸。物理设备的尺寸随显示器、打印机或绘图仪所设置的尺寸而变化。
     转换
如要在物理设备上绘制输出,Windows把一个矩形区域从一个坐标空间拷贝到(或映射到)另一个坐标空间,直至最终完整的输出呈现在物理设备上(通常是屏幕或打印机)。
如果该应用程序调用了SetWorldTransform函数,那么映射就从应用程序的世界坐标系空间开始;否则,映射在页面空间中进行。在Windows把矩形区域的每一点从一个空间拷贝到另一个空间时,它采用了一种被称作转换的算法,转换是把对象从一个坐标空间拷贝到另一个坐标空间时改变(或转变)这一对象的大小、方位、和形态,尽管转换把对象看成一个整体,但它也作用于对象中的每一点或每条线。
Msdn有例子。
              ※页面空间到设备空间的转换(主要)
页面空间到设备空间的转换是原Windows接口的一部分。这种转换确定与一特定设备描述表相关的所有图形输出的映射方式。
(逻辑到设备)
所谓映射方式是指确定用于绘图操作的单位大小的一种量度转换。映射方式是一种影响几乎任何客户区绘图的设备环境属性。另外还有四种设备环境属性:窗口原点、视口原点、窗口范围和视口范围,这四种属性与映射方式密切相关。
 
页面空间到设备空间的转换所用的是两个矩形的宽与高的比率,其中页面空间中的矩形被称为窗口,设备空间中的矩形被称为视口,Windows把窗口原点映射到视口原点,把窗口范围映射到视口范围,就完成了这种转换,如下图所示:

#设备空间到物理空间的转换
设备空间到物理空间的转换有几个独特之处:它只限于平移,并由Windows的窗口管理部分控制,这种转换的唯一用途是确保设备空间的原点被映射到物理设备上的适当点上。没有函数能设置这种转换,也没有函数可以获取有关数据。
              #默认转换
一旦应用程序建立了设备描述表,并立即开始调用GDI绘图或输出函数,则运用默认页面空间到设备空间的转换和设备空间到客户区(物理设备空间)的转换(在应用程序调用SetWorldTransform函数之前,不会出现世界坐标空间到页面空间的转换)。
默认页面空间到设备空间的转换结果是一对一的映射;即页面空间上给出的一点映射到设备空间的一个点。正如前文讲到的,这种转换没有以矩阵指定,而是通过把视口宽除以窗口宽,把视口高除以窗口高而得出的。在默认的情况下,视口尺寸为1x1个象素,窗口尺寸为1x1页单位。
设备空间到物理设备(客户区、桌面或打印机)的转换结果总是一对一的;即设备空间的一个单位总是与客户区、桌面、或打印机上的一个单位相对应。这一转换的唯一用途是平移。无论窗口移到桌面的什么位置,它永远确保输出能够正确无误地出现在窗口上。
默认转换的一个独特之处是设备空间和应用程序窗口的y轴方向。在默认的状态下,y轴正向朝下,负y方向朝上
              #逻辑坐标和设备坐标
几乎在所有的GDI函数中使用的坐标值都是采用的逻辑单位。Windows必须将逻辑单位转换为“设备单位”,即像素。这种转换是由映射方式、窗口和视口的原点以及窗口和视口的范围所控制的。
Windows对所有的消息(如WM_SIZE、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP),所有的非GDI函数和一些GDI函数(例如GetDeviceCaps函数),永远使用设备坐标。
“窗口”是基于逻辑坐标的,逻辑坐标可以是象素、毫米、英寸等单位;“视口”是基于设备坐标(象素)的。通常,视口和客户区是相同的。
缺省的映射模式为MM_TEXT。在这种映射模式下,逻辑单位和设备单位相同。
              #SetMapMode改变映射方式
              #逻辑坐标和设备坐标的相互转换
窗口(逻辑)坐标转换为视口(设备)坐标的两个公式:
xViewport=(xWindow-xWinOrg)* xViewExt/xWinExt +xViewOrg
// xViewExt视口的x范围xWinExt窗口的x范围,相除为转换因子
yViewport=(yWindow-yWinOrg)*xViewExt/xWinExt+yViewOrg
 
视口(设备)坐标转换为窗口(逻辑)坐标的两个公式:
xWindow=(xViewPort-xViewOrg)*xWinExt/xViewExt+xWinOrg
 
yWindow=(yViewPort-yViewOrg)*xWinExt/xViewExt+yWinOrg
 
在MM_TEXT映射方式下逻辑坐标和设备坐标的相互转换:
窗口(逻辑)坐标转换为视口(设备)坐标的两个公式:
xViewport = xWindow-xWinOrg+xViewOrg
yViewport = yWindow-yWinOrg+yViewOrg
视口(设备)坐标转换为窗口(逻辑)坐标的两个公式:
xWindow = xViewport-xViewOrg+xWinOrg
yWindow = yViewport-yViewOrg+yWinOrg
//转换因子为1
 
#设置SetScrollSizes 01:15
void SetScrollSizes(
   int nMapMode,//映射模式
   SIZE sizeTotal,
       //滚动视图的总尺寸
   const SIZE& sizePage = sizeDefault,
//可以用CSize,点击滚动空白处的时候滚动的数量
   const SIZE& sizeLine = sizeDefault
       //点击滚动的箭头的时候滚动的数量
);
//什么时候调用呢?窗口创建之后调用
//使用OnInitialUpdate(该函数是当第一个视图和文档关联之后被调用)(虚函数)
//而且它在第一次调用OnDraw的调用之前
void CGraphicView::OnInitialUpdate()
{
       CScrollView::OnInitialUpdate();
       // TODO: Add your specialized code here and/or call the base class
       SetScrollSizes(MM_TEXT,CSize(800,600));
}
 
//?当移动到窗口下端划线,窗口重绘时所画的线条位置发生了改变
//坐标点并没有发生改变
//但我们作图的时候使用的是逻辑坐标,windows需要将逻辑坐标改变为设备坐标来输出图形
//而OnPrepareDC(&dc)用来调整显示上下文的属性
//可能就是它改变了上下文属性
//在MFC中查找OnPrepareDC函数(viewscrl.cpp)(先搜索CSrollView)
其中
       switch (m_nMapMode)
       {
       case MM_SCALETOFIT:
              pDC->SetMapMode(MM_ANISOTROPIC);
              pDC->SetWindowExt(m_totalLog); // window is in logical coordinates
              pDC->SetViewportExt(m_totalDev);
              if (m_totalDev.cx == 0 || m_totalDev.cy == 0)
                     TRACE0("Warning: CScrollView scaled to nothing./n");
              break;
       default://设置了MM_TEXT就到这里
              ASSERT(m_nMapMode > 0);
              pDC->SetMapMode(m_nMapMode);
              break;
       }
       CPoint ptVpOrg(0, 0);       // assume no shift for printing
       if (!pDC->IsPrinting())//是否在打印中?不在打印中则执行
       {
              ASSERT(pDC->GetWindowOrg() == CPoint(0,0));
 
              // by default shift viewport origin in negative direction of scroll
              ptVpOrg = -GetDeviceScrollPosition();//返回值有符号
 
              if (m_bCenter)
              {
                     CRect rect;
                     GetClientRect(&rect);
 
                     // if client area is larger than total device size,
                     // override scroll positions to place origin such that
                     // output is centered in the window
                     if (m_totalDev.cx < rect.Width())
                            ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2;
                     if (m_totalDev.cy < rect.Height())
                            ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2;
              }
       }
       pDC->SetViewportOrg(ptVpOrg);//设置视口的原点
 
#视口和窗口原点的改变01:25?????????????????????
CDC中提供了两个成员函数函数SetViewportOrg和SetWindowOrg,用来改变视口和窗口的原点。(获取则为Get……)
如果将视口原点设置为(xViewOrg,yViewOrg)若(0,-150),则逻辑点(0,0)就会被映射为设备点(xViewOrg,yViewOrg)(设备坐标(相对于左上角))(0,-150)。如果将窗口原点改变为(xWinOrg,yWinOrg)则为(0,150),则逻辑点(xWinOrg,yWinOrg)将会被映射为设备点(0,0),即左上角。
不管对窗口和视口原点作什么改变,设备点(0,0)始终是客户区的左上角。
#图形错位的说明
当我们在窗口中点击鼠标左键的时候,得到的是设备坐标(680,390),在MM_TEXT的映射模式下,逻辑坐标和设备坐标单位是相等的,所以我们利用集合类保存的这个点的坐标是以象素为单位,坐标值为(680,390)。在调用OnDraw函数前,在OnPaint函数中调用了OnPrepareDC函数,调整了显示上下文的属性,将视口的原点设置为了(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)转换为逻辑坐标,根据设备坐标转换为逻辑坐标的公式:
     xWindow = xViewport-xViewOrg+xWinOrg
     yWindow = yViewport-yViewOrg+yWinOrg,得到逻辑点的x坐标为680-0+0=680,y坐标为390-(-150)+0=540,将逻辑坐标(680,540)保存起来,在窗口重绘时,会先调用OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为了(0,-150),然后GDI函数用逻辑坐标点(680,540)绘制图形,被Windows转换为设备坐标点(680,390),和原先显示图形时的设备点是一样的,当然图形就还在原先的地方显示出来。
代码:在保存坐标之前做如下转换:
       OnPrepareDC(&dc);
       dc.DPtoLP(&m_ptOrigin);
       dc.DPtoLP(&point);
#OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点。
 
原创粉丝点击