[MFC]映射模式、坐标转换、原点移动

原创 2015年07月08日 13:11:45

1. 映射模式的概念:

    1) 也是DC的重要属性之一,它和绘图时的尺寸有关;

    2) 在没有映射模式的时候绘图只能以像素点为单位长度进行绘图,但是这样做有一个明显的坏处就是,不同显示器分辨率、屏幕大小各不相同,在一个屏幕上画1000像素长的线很长,同样像素点的长度换到另一个屏幕上看上去却可能很短,这无法达到不同屏幕绘制的图形效果相同的目的,因此要引入映射模式这个功能来解决上述问题;

    3) 映射模式规定,在利用GDI绘图的时候统一使用逻辑长度单位,然后通过某种方法(公式)将逻辑尺寸映射成实际物理屏幕的像素点长度,不同映射模式决定了一个逻辑单位代表多少个像素点长;

    4) 映射模式的两个对象:

         i. 逻辑坐标:即传递给绘图API函数的坐标值,是一种抽象、统一、设备无关的人为假定的坐标值;

         ii. 设备坐标:即屏幕上的具体像素点坐标;

         iii. 映射模式决定了如何从逻辑坐标转化成设备坐标,因为具体的物理设备只认识像素点位置这个东西,就好像计算机只认识01一样;

    5) 映射模式属性的操作:SetMapMode、GetMapeMode,默认值是MM_TEXT,MM即MapMode的缩写,默认的MM_TEXT映射模式不做任何映射,逻辑单位1就代表1个像素点;


2. 常用的GDI映射模式:

    1) 有了映射模式就可以选择诸如“一逻辑单位等于1厘米”之类的映射模式了,这样则在绘图时长度、尺寸等就和屏幕分辨率无关了,不同屏幕之间的调整完全都是自动的,MFC内部全部都实现了,比如在一块屏幕上一厘米等于100个像素点,而另一块屏幕上一厘米等于135个像素点,这这些映射工作MFC都在内部自动完成了,这样就可以实现按比例的缩放,并且与屏幕分辨率、大小无关;

    2) 常用的映射模式:其中原点都是左上角,x轴都是朝右的

MM_TEXT:1 pixel / 1 lg,y+

MM_LOMETRIC:0.1 mm / 1 lg,y-

MM_HIMETRIC:0.01 mm / 1 lg,y-

MM_LOENGLISH:0.01 in / 1 lg,y-

MM_HIENGLISH:0.001 in / 1 lg,y-

MM_TWIPS:1/1440 in / 1 lg,y-

!!0.01 in / 1 lg就表示1逻辑单位但与0.01英寸,一起类推,lg就是logical的意思,而y+表示y轴朝下,y-表示y轴朝上;

!!公制映射模式:就是指以上所有y-的映射模式,公制就是指国际定标的长度单位,如厘米、米、英寸等,并且公制习惯坐标轴的y轴朝上;

!!所以一定要注意,在使用公制映射模式时y轴坐标一定要传负的,否则图形会不见的!

!!同样也要注意,在公制和非公制的映射模式之间转换的时候,y轴坐标一定要取反,否则模式改变后绘制的图形也会不见的!

    3) 用户自定义类型映射模式:

         i. 有两种,这两种的映射比例可以自定义,而且x轴和y轴的方向也可以自定义;

         ii. MM_ISOTROPIC:各向同性,x轴和y轴的映射比例必须相同;

         iii. MM_ANISOTROPIC:各向异性,x轴和y轴的映射比例可以不同;


3. 可编程映射模式:

    1) 即用户自定义映射模式,即MM_ISOTROPIC和MM_ANISOTROPIC这两种;

    2) 一个典型的用法:

GetClientRect(&rcClient); // 在改变模式以前是MM_TEXT,即逻辑单位等于像素单位
dc.SetMapMode(MM_ANISOTROPIC); // 以各向异性为例
dc.SetWindowExt(500, 500); // 指定逻辑窗口为500×500的逻辑单位
dc.SetViewportExt(rcClient.Width(), rcClient.Height()); // 指定设备视口为width×height的像素单位
// 完成后,x轴比例为(width/500) pixel / 1 lg,y轴比例为(height/500) pixel / 1 lg
dc.Ellipse(0, 0, 500, 500); // 所有的绘图输入的都是逻辑单位
!其中Window Port为窗口,View Port为视口,窗口使用的是逻辑单位,而视口使用的是设备尺寸,单位即为像素;

    3) 设置比例的两个函数:

         i. virtual CSize CDC::SetWindowExt(int cx, int cy);

         ii. virtual CSize CDC::SetViewportExt(int cx, int cy);

!!两者的返回值都是修改之前的cx和cy,CSize其实就是一个结构体,里面有cx和cy两个域;

!!cx为宽,cy为高,参数也可以直接传CSize或SIZE结构体,MFC都重载过了;

!!Ext即为Extend的缩写,即范围的意思,只不过SetWindowExt设定的是逻辑窗口的范围,而SetViewportExt设定的是设备视口的范围(单位为像素点);

    4) 上面的这个例子只是自定义了映射比例,但是没有自定义轴的方向,默认情况下x轴朝右y轴朝下,如果想让某一个轴反向,只需要在两个SetExt函数中选一个为目标轴的参数传负数即可,注意!!必须是两选一,不能两个都函数的那个参数都传负数,选哪个函数无所谓,都是对称的,比如:

SetWindowExt(-500, 500);
SetViewportExt(10000, 10000);
!这就让x轴反向了,y轴反向同理;

    5) ANISOTROPIC和ISOTROPIC的区别:后者强制要求x轴和y轴的映射比例相同,如果在使用两个SetExt函数时故意让两个比例不相同,则MFC将强制将比例设为“逻辑单位:设备单位”较大的那个;

!!ISOTROPIC特别适合画圆、正方形等正则图形,当然MM_TEXT本身也是各向同性的,x轴和y轴都是1:1的像素点;


4. 坐标转换:

    1) 问题背景:设想,你一直在一种公制的映射模式下编程,但是诸如鼠标击中测试之类的消息携带的击键坐标都是设备坐标(即像素点的位置),想要知道鼠标是否击中了某个区域那肯定得现将击中的设备坐标转化成公制坐标后才能进行判断,不可能拿设备坐标和公制坐标这两个不能相提并论的东西进行比较吧;

    2) 逻辑坐标和设备坐标转换的函数:

         i. 逻辑坐标转化成设备坐标:void CDC::LPtoDP(LPPOINT lpPoints, int nCount = 1) const; 

         ii. 设备坐标转化成逻辑坐标:void CDC::DPtoLP(LPPOINT lpPoints, int nCount = 1) const;

!!LP即Logical Points的简称,DP即Device Points的简称;

!!第一个参数是一个点的数组,也可以是CPoint的数组,第二个参数是要转换的点的个数,结果直接修改在原数组中,默认情况下就只有一个点;

    3) 示例:

原设备坐标,欲得到LOENGLISH的公制坐标,目标是窗口中心位置:

CRect rcClient;
GetClientRect(&rcClient);
CPoint point(rcClient.Width() / 2, rcClient.Height() / 2);
CClientDC dc(this);
dc.SetMapMode(MM_LOENGLISH);
dc.DPtoLP(&point);
原设备坐标,欲得到公制LOENGLISH坐标(100, 100)对应的设备坐标:

CPoint point(100, 100);
CClientDC dc(this);
dc.SetMapMode(MM_LOENGLISH);
dc.LPtoDP(&point);


5. 移动原点:

    1) 问题背景:就像通常的数学研究都是将坐标系放在平面中心或者平面左下角,而不是一般计算机画面的左上角,因此有时为了方便或者说是顺眼,需要将原点移动到其它位置;

    2) 有两种方法:一种是移动逻辑窗口的原点,还有一种是移动设备视口的原点,两者只要移动其中之一即可,不能同时移动这两个口的原点,否则会混乱的

         i. 移动逻辑窗口原点:CPoint CDC::SetWindowOrg(int x, int y);

         ii. 移动设备视口原点:virtual CPoint CDC::SetViewportOrg(int x, int y);

         iii. 返回值是修改前的原点坐标,参数还可以穿CPoint和POINT结构体,Org即Origin“原点”的缩写;

!!为什么两者只需要设定一个就可以了,因为只要一个设定过了,则MFC底层的映射公式就会自动将两个口的原点的移动统一在一起了;

!!比如,利用SetViewOrg将视口的(0, 0)移动到了(x, y),这就相当于将逻辑点(0, 0)映射到了设备点(x, y)上,因此不用再移动逻辑点,里面的映射关系已经包含了;

!!若利用SetWindowOrg将逻辑点(0, 0)移动到了(x, y),就相当于将逻辑点(x, y)映射到了设备点(0, 0)上,所有的一切都可以用逻辑坐标到设备坐标的映射上来;

    3) 示例:假设目前处于MM_TEXT,接下来要在MM_HIMETRIC模式下画图,并且原点要在平面的左下角,那么根据上面说的只需设定一个口即可,通常设定视口最方便,因此范例代码如下

CRect rcClient;
GetClientRect(&rcClient);
dc.SetViewportOrg(0, rcClient.Height);
dc.SetMapMode(MM_HIMETRIC);
...


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

MFC各种坐标转换

在某个视图的WM_LBUTTONDOWN消息响应函数下做了一些试验,验证各种坐标系下的坐标转换。 注意:这里的窗口指 该视图以及包围它的边框所组成的区域。

窗口、视口、屏幕显示详解--计算机绘图基本功

窗口、视口、屏幕显示详解--计算机绘图基本功             窗口: 逻辑环境中的一小部分,是一个矩形框;世界坐标系是逻辑坐标,SetWindowOrg(X,Y )设置窗口的逻辑坐标点(X,...

MFC创建悬浮窗口

1. 创建悬浮窗口类 每一个悬浮窗口都是一个CDockablePane的派生类的对象, 因此要为每一个悬浮窗口创建一个新类 1.1 添加类 通过菜单Project->Add Class...或者...

【MFC】【停靠窗口】的建立及其内部【控件的嵌入】

以VS2010为例,创建一个MFC多文档应用程序,在第二步Aplication Type中的Project style选Visual Studio风格。然后点Finish。 这个模板程序创建了Fil...

MFC下CSocket编程详解

MFC下CSocket编程详解:  1. 常用的函数和注意事项(详细的函数接口说明请查看MSDN):     CSocket::Create 初始化(一般写服务器程序都不要用为好,用下面的 CSo...

MFC界面 坐标获取/转换 及 区域获取

MFC界面相关常用方法整理:   坐标点----CPoint: CPoint   构造方法: CPoint(); CPoint( intinitX, int initY ); CPoin...

MFC下CSocket编程详解

MFC下CSocket编程详解: 1. 常用的函数和注意事项(详细的函数接口说明请查看MSDN):    CSocket::Create 初始化(一般写服务器程序都不要用为好,用下面的 CSocket...

MFC的坐标转换GetClientRect/GetWindowRect/ClientToScreen/GetCursorPos/ScreenToClient

(windows的窗口左上点为基点,也即原点,向右x轴正向,向下y轴正向)   1. 像MFC传入的point而言,他们的取值为相对的坐标(以当前窗口左上点为基点的相对坐标)    void CDlg...

MFC中获取【文档】【视图】【框架】【应用程序】指针的方法

对于初学者来说,在MFC中获得各种类的指针是个障碍。如:要获得另一窗体视图类中的某控件指针,首先必须获得目标视图指针,然后再通过该视图指针用GetDlgItem函数才可获得控件指针。  只考虑文档、...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)