MFC GDI坐标映射例解

本文通过实例详细解析了MFC绘图中不同映射模式下,逻辑坐标到设备坐标的转换方法,并展示了窗口大小变化对图形的影响。

1)建立单文档MFC项目DrawNewàProjectsàMFC AppWizard(EXE)àSingle Document

2)找到CMainFrame::PreCreateWindow函数,在其中设置默认窗口大小为400 pixel*300 pixel

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

if( !CFrameWnd::PreCreateWindow(cs) )

return FALSE;

// TODO: Modify the Window class or styles here by modifying

// the CREATESTRUCT cs

cs.cx=400;

cs.cy=300;

return TRUE;

}

3)添加OnPaint事件

资源管理器àClassViewà右击CDrawView 选择Add Windows Message Handler

àWM_PAINTàAdd Handler

1. void CDrawView::OnPaint()

2. {

3. CPaintDC dc(this); // device context for painting

4. // TODO: Add your message handler code here

5. CRect cr;//矩形结构

6. GetClientRect(&cr);//获得客户区窗口

7. int cx=cr.right;//

8. int cy=cr.bottom;//

9. dc.SetMapMode(MM_ISOTROPIC);//X=Y

10. dc.SetWindowExt(1000,1000);//设置逻辑窗口,默认窗口原点为(00

11. dc.SetViewportExt(cx,-cy);//定义输出视口,XY上为正

12. dc.SetViewportOrg(cx/2,cy/2);//定义视口原点为客户区中心

13. dc.Ellipse(-200,200,200,-200);//绘制椭圆与客户区外切的椭圆

14. //绘制水平垂直的四条半径

15. dc.MoveTo(0,0); dc.LineTo(200,0);

16. dc.MoveTo(0,0); dc.LineTo(-200,0);

17. dc.MoveTo(0,0); dc.LineTo(0,200);

18. dc.MoveTo(0,0); dc.LineTo(0,-200);

19. //执行F5进行Debug,在底端Output窗口中可以观察ClientRect

20. TRACE( "ClientRect.x = %d, ClientRect.y = %d/n", cx, cy );

21. }

运行结果如图1左。当改变窗口大小时,图中圆形状始终不变。

<1>将上面代码的第9行改为:dc.SetMapMode(MM_ANISOTROPIC);//X!=Y运行结果如图1右。

1

我们发现,尽管上面代码的第13dc.Ellipse(-200,200,200,-200);中定义的椭圆外接矩形逻辑上为正方形,但是显示的并不是圆,而是椭圆。

当我们改变窗口大小时,图中椭圆变形,甚至可能变为圆形。具体为:

保持窗口宽度不变时,减小高度,椭圆变得更扁;保持窗口高度不变时,减小宽度,椭圆变得更圆,当拉伸到客户区为正方形时,我们发现椭圆变成了圆!

<2>将上面代码的第9行改回dc.SetMapMode(MM_ISOTROPIC);//X=Y,第15行改为dc.LineTo(500,0); 18行改为dc.LineTo(0,-500); 运行结果如图2左。

保持窗口高度不变,减小窗口宽度,使窗口宽度<窗口高度,运行结果如图2右。

2

<3>在将<2>中代码的第9行改回dc.SetMapMode(MM_ANISOTROPIC);//X!=Y,运行结果如图3

3

当我们改变窗口大小时,dc.LineTo(500,0); dc.LineTo(0,-500);都是由原点(客户区中心)到客户区右端中心、底端中心的直线。

<4>将原代码中第10dc.SetWindowExt(1000,1000);//设置逻辑窗口后添加dc.SetWindowOrg(100,100);设置逻辑窗口的原点为(100100)。观察运行结果可知,图1中的图形整体向左向下分别移动了100个逻辑单位:

(-200,200,200,-200)——>(-200-100,200-100,200-100,-200-100)

若需要保持图1中的图形,则需要将涉及到的每个点加上(100,100),即:

13. dc.Ellipse(-200+100,200+100,200+100,-200+100);

14. //绘制水平垂直的四条半径

15. dc.MoveTo(0+100,0+100); dc.LineTo(200+100,0+100);

16. dc.MoveTo(0+100,0+100); dc.LineTo(-200+100,0+100);

17. dc.MoveTo(0+100,0+100); dc.LineTo(0+100,200+100);

18. dc.MoveTo(0+100,0+100); dc.LineTo(0+100,-200+100)

(4)总结逻辑窗口坐标到设备视口坐标的映射方法:

<1>逻辑窗口原点映射为视口原点

<2>逻辑窗口宽度和高度映射为视口宽度和高度

<3>当映射方式为MM_ISOTROPIC时,WindowExt.Width=WindowExt.Height,有效绘图区域为以视口宽高中的最小边为边长的正方形区域。比例因子为:

scaleX=scaleY=min{ViewportExt.Width, ViewportExt.Height }/WindowExt.Width

当映射方式为MM_ANISOTROPIC时,有效绘图区域为整个视口(这里为客户区)。比例因子为:

scaleX=ViewportExt.Width/WindowExt.Width

scaleY=ViewportExt.Height /WindowExt.Height。见图4.

<4>设备(视口)坐标 = (逻辑坐标逻辑窗口原点坐标)×比例因子+视口原点坐标

4

以下分析中客户区大小为ClientRect=(388200),逻辑窗口原点为WindowOrg=(100,100),基于(3<4>中修改后的代码。

在上图4左中,nMapMode=MM_ISOTROPIC,椭圆外接矩形左上角逻辑坐标(-100,300)映射为客户区的以Pixel为单位的坐标为:

left_top_X= (-100-100)×(200/1000+388/2=154 pixel

left_top_Y= (300-100)×(200/1000+200/2=140 pixel

依此次方法可计算出右下角逻辑坐标(300,-100)映射为客户区的以Pixel为单位的坐标为:

right_bottom_X=234 pixel;right_bottom_Y=60 pixel

我们若在第9dc.SetMapMode(MM_ISOTROPIC);//X=Y前添加CreatePen(PS_SOLID,2,RGB(255,0,0));dc.Ellipse(154,140,234,60);则可以发现,这个以2个像素宽的红色画笔绘制的()圆刚好和设置映射模式后绘制的()圆重合。但是我们改变窗口大小时,发现设置映射模式后绘制的()圆按比例拉伸,但红色圆始终在原地且大小保持不变,这也说明了默认映射方式MM_TEXT是以X轴正方向朝右,Y轴正方向朝下的坐标系和1 pixel为单位进行绘制的。

同理,我们可以分析上图4右中,nMapMode=MM_ANISOTROPIC的情况下,CRect(116,140,272,60);为等效椭圆外接矩形。

/*=============================================== 作者:LXZ-2008 FROM:CUMT 计08级 时间:2012-04-22 功能:能在SDK、MFC编程中实现笛卡尔 坐标系统的绘制,以及曲线,点的绘制。 特性:1.本程序采用面向对象思想设计; 2.具备很好的独立性,随时可以把这两个文件应 用在任意SDK、MFC开发中; 3.有良好的灵活性,扩展性,易用性,在稍微扩 展一下可以绘制任意曲线,图形; 4.具备良好的组合性,符合模块内高内聚,模块 外低耦合的思路; 5.整个程序仅有1300行左右代码,如果嫌代码过 多,可以把原先变量的PROTECTED保护类型打开, 换成PUBLIC,这样去掉GET和SET函数,这个思路 起源于我对J2EE中STRUTS2框架的学习以及对COM 组件技术的了,它们也是这种思路这时可以省 下几百行代码。 6.当然也会有设计模式的思路在里面。 个人说明: 本程序花了我将近2天的时间编写,尽管开始有点 不想,但是还是觉得有意义,能给广大网友提供益处。 本系统的雏形来自2010年下半年的程序,当时花了 10天时间,弄了3千行代码。在现在看来当时的程序的 执行效率未必比现在的低,但是可维护性糟糕,可拓展 性糟糕,不具备良好的灵活性。需求改变了,代码会大 幅改变。换句话说,现在看来当时的程序是十分糟糕的, 生命周期已经结束。 而在用了面向对象的思想和设计模式,以及一些数 据结构去重新搭建这个系统的时候,代码其实1千多行就 搞定了,时间3-4天,不需要那么多(现在我来弄的话)。 主要起源于自己参与真实的有数十万代码的项目的开发, 这样提高了对程序开发的认识,以及商业程序应该如何 开发。同时也是自己面向对象思想和设计模式学习,对 自身思想的提高。 希望阅读代码的人觉得这些代码是优雅的,这就满 足了,尽管注释少了些,你们自己加吧。 QQ:706625262 E-MAIL:706625262@qq.com 不做商业和技术支持。 声明: 本程序代码未经本人同意,或者未给我MONEY的前提下, 不得用于商业目的,别让我鄙视你。在非商业目的使用 下请注明本人是原创,表学腾讯。 ==================================================*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值