VC中坐标系的建立

建立一个合适的坐标系可以为我们的绘图带来很大的方便 。下面介绍一下如何在VC中建立我们想要的坐标系。
一 设备坐标和逻辑坐标
  设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。
  逻辑坐标(Logical Coordinate)是系统用作记录的坐标。在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:
  1. 窗口为非滚动窗口
  2. 窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。
  在VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻辑坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用CDC 函数DptoLP()将设备坐标转化为逻辑坐标,同样可以用LptoDP()将逻辑坐标转化为设备坐标。

  二 坐标模式
  为了在不同的领域使用逻辑坐标,Windows提供了以下8种坐标模式:
  分别为MM_TEXT、MM_HIENGLISH、MM_LOENGLISH、MM_HIMETRIC、MM_LOMETRIC、MM_TWIPS、MM_ANISOTROPIC和MM_ISOTROPIC。 

  三 实例解析
  (一) 建立以左上角为原点,X轴和Y轴为1000的坐标,如下图
  我们可以用以下代码:
  void CTtView::OnDraw(CDC* pDC)
   {
    CTtDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rect;
    GetClientRect(&rect);

    pDC->SetMapMode(MM_ANISOTROPIC);
    pDC->SetViewportOrg(0,0);
    pDC->SetViewportExt(rect.right,rect.bottom);

    pDC->SetWindowOrg(0,0);
    pDC->SetWindowExt(1000,1000);

    pDC->MoveTo(50,50);
    pDC->LineTo(50,950);
    pDC->LineTo(950,950);
    pDC->LineTo(50,50);
   }
  代码分析:
  1. GetClientRect(&rect); 取得客户区矩形区域,将其存放在rect中
  2. 用pDC->SetMapMode(MM_ANISOTROPIC); 设置映射模式
  3. 通过pDC->SetViewportOrg(0,0);设置逻辑坐标的原点。
  4. 通过pDC->SetViewportExt(rect.right,rect.bottom);和 
pDC->SetWindowExt(1000,1000);来确定逻辑坐标下和设备坐标下的尺寸对应关系
  5. 在MM_ANISOTROPIC模式下,X轴单位和Y轴单位可以不相同
  6. 坐标方向的确定方法是如果逻辑窗范围和视口范围符号相同,则逻辑坐标的方向和视口的方向相同,即X轴向右为正,Y轴向下为正。
  7. 如果将显示模式改为MM_ISOTROPIC,那么X轴单位和Y轴单位一定相同,感兴趣的读者可以自己使一下。
  (二) 建立以视窗中心为原点的坐标,如下:
  用如下代码:
  void CTtView::OnDraw(CDC* pDC)
   {
    CTtDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rect;
    GetClientRect(&rect);

    pDC->SetMapMode(MM_ANISOTROPIC);
    pDC->SetViewportOrg(rect.right/2,rect.bottom/2);
    pDC->SetViewportExt(rect.right,rect.bottom);

    pDC->SetWindowOrg(0,0);
    pDC->SetWindowExt(1000,-1000);

    pDC->MoveTo(150,150);
    pDC->LineTo(-150,-200);
    pDC->LineTo(150,-150);
    pDC->LineTo(150,150);
   }
 代码分析:
  1. 用 pDC->SetViewportOrg(rect.right/2,rect.bottom/2); 设置视口的原点。
2. 用pDC->SetViewportExt(rect.right,rect.bottom);和pDC->SetWindowExt(1000,-1000);来确定设备坐标和逻辑坐标的单位对应关系。
  3. 因为逻辑窗范围和视口范围的符号不一致,纵坐标取反,所以Y轴向上为正。

MM_LOENGLISH、MM_HIENGLISH、MM_LOMETRIC、MM_HIMETRIC、MM_TWIPS这一组是Windows提供的重要的固定比例映射模式。 

它们都是x值向右方向递增,y值向下递减,并且无法改变。它们之间的区别在于比例因子见下:(我想书上P53页肯定是印错了,因为通过程序实验x值向右方向也是递增的) 

MM_LOENGLISH 0.01英寸 
MM_HIENGLISH 0.001英寸 
MM_LOMETRIC 0.1mm 
MM_HIMETRIC 0.01mm 
MM_TWIPS 1/1440英寸 //应用于打印机,一个twip相当于1/20磅,一磅又相当于1/72英寸。

 

在VC里, 准确的说是在Windows编程中,映射模式有:MM_TEXT,(设备坐标)
MM_HIMETRIC,(逻辑坐标)
MM_ISOTROPIC,MM_ANISOTROPIC,.....在的开发环境下,(Windows 默认的模式是MM_TEXT),我们可以通过SetMapMode()来改变当是的映射模式~
   MM_TEXT :X向右方向递增加,Y向下方向递增加,我们可以通过SetViewportOrg()和SetWindowOrg()来改变坐标原点的位置.
 void CInSide_VCView::OnDraw(CDC* pDC)
{
 pDC->TextOut (0,0,"TEST");
 pDC->SelectStockObject (GRAY_BRUSH);
 pDC->SetMapMode (MM_TEXT);
 //pDC->SetWindowOrg (100,100);  交替使用这两句看有什么现象
 //pDC->SetViewportOrg(CPoint(100,100));
 pDC->Ellipse (CRect(0,0,300,300));
 pDC->TextOut (0,0,"22");
 
}
   固定比例模式:X向右方向递减(我认为是增加),Y向下方向递减,MM_LOENGLISH(0.01英寸) MM_HIENGLISH(0.001英寸) MM_LOMETRIC(0.1mm) MM_HIMETRIC(0.01mm) MM_TWIPS(1/1400英寸,一般用于打印机)
   可变比例模式:M_ISOTROPIC(1:1),MM_ANISOTROPIC(可为任意比例),用SetWindowsExt() 和SetViewportExt()来设定比例,
void CInSide_VCView::OnDraw(CDC* pDC)
{
        CRect rectClient;
 GetClientRect(rectClient);
 pDC->SelectStockObject (m_nColor);
 pDC->SetMapMode (MM_ANISOTROPIC);
 pDC->SetWindowExt (1000,1000);
 pDC->SetViewportExt (rectClient.right,rectClient.bottom );
 pDC->SetViewportOrg (rectClient.right/2,rectClient.bottom/2);
 pDC->Ellipse (CRect(-500,-500,500,500));
}
  物理坐标,即我们现实中的尺寸,屏幕中一英寸是现实中的12英寸,如果我们使用MM_LOENGLISH(0.01英寸)的映射模式,那么26.75英寸将是电脑的26.75/12=2.23(英寸),在电脑里1个逻辑单位是0.01英寸,所以,26.75英寸转换为逻辑单位是223单位,但是这过程中有省略,为了防止这样的情况,我们可以使用物理坐标保存。

  转换函数;DPtoLP() 设备坐标到逻辑坐标; LPtoDP()逻辑坐标到设备坐标;物理坐标到逻辑坐标的转换全是由我们自己计算;

MFC 中一般情况下有下面的情况:
  CDC类中的所有成员函数用的是逻辑坐标做参数
  CWND类中的所有成员函数用的是设备坐标做参数
  所有的选中-测试(HIT-TEST)操作都是用的设备坐标,有些函数只能使用设备坐标如:CRect::PtInRect()
  所有要长期保存的值一般用逻辑坐标,用设备坐标则用户对窗口滚动就会改变,该坐标就会失效了;

 下一例子,测试鼠标左键点的区域是不是在指定的地方
void CMyView::OnLButtonDown(UINT uFlags,CPoint point)
{
     CRect rect=m_rect;
     CClientDC dc(this);
     dc.SetMapMode(MM_TEXT);
     dc.LPtoDP(rect);
     if(rect.PtInRect(point))
       {
          MessageBox("YES");
       }

}

一般设置映射模式在OnPrepareDC() 中比较合适~,OnPrepareDC在OnDraw前调用!
 

MFC 对140 种Windows 消息提供了直接的消息控制函数,特别注意下面5个:WM_CREATE,WM_CLOSE,WM_DESTROY,WM_NCDESTROY,WM_QUERYENDSESSION.

WM_CREATES 是WINDOWS 发送给视图的第一个消息,所以在OnCreate()中不能调用那写依赖窗口完全处于激活的WINDOWS函数!,一般来说可以在 OnInitialUpdate()中调用,如:设置映射模式~,但是必须注意,OnInitialUpdate()在视图生存之间可能被调用多次
MSDN :
Call SetScrollSizes when the view is about to be updated.
void SetScrollSizes(
   int nMapMode,
   SIZE sizeTotal,
   const SIZE& sizePage = sizeDefault,
   const SIZE& sizeLine = sizeDefault
);
Parameters
nMapMode
The mapping mode to set for this view. Possible values include:
Mapping Mode Logical Unit Positive y-axis Extends...
MM_TEXT 1 pixel Downward
MM_HIMETRIC 0.01 mm Upward
MM_TWIPS 1/1440 in Upward
MM_HIENGLISH 0.001 in Upward
MM_LOMETRIC 0.1 mm Upward
MM_LOENGLISH 0.01 in Upward
 
 
 
 
All of these modes are defined by Windows. Two standard mapping modes, MM_ISOTROPIC and MM_ANISOTROPIC, are not used for CScrollView. The class library provides the SetScaleToFitSize member function for scaling the view to window size. Column three in the table above describes the coordinate orientation.
sizeTotal  //SCrollbar的显示范围,含不在视区显示部分的大小
The total size of the scroll view. The cx member contains the horizontal extent. The cy member contains the vertical extent. Sizes are in logical units. Both cx and cy must be greater than or equal to 0.
sizePage //当点击Scrollbar空白条时,视区移动得大小
The horizontal and vertical amounts to scroll in each direction in response to a mouse click in a scroll-bar shaft. The cx member contains the horizontal amount. The cy member contains the vertical amount.
sizeLine //点击SCrollbar的上下左右按钮时,视区移动的大小
The horizontal and vertical amounts to scroll in each direction in response to a mouse click in a scroll arrow. The cx member contains the horizontal amount. The cy member contains the vertical amount.
 如下实例:
void CEx04cView::OnInitialUpdate()
...{
        CScrollView::OnInitialUpdate();
        CSize sizeTotal(20000, 30000); // 20 by 30 cm
        CSize sizePage(sizeTotal.cx / 4, sizeTotal.cy / 4); //当点击Scrollbar空白条时,视区移动得大小
        CSize sizeLine(sizeTotal.cx / 20, sizeTotal.cy / 20); //点击SCrollbar的上下左右按钮
        SetScrollSizes(MM_TEXT, sizeTotal, sizePage, sizeLine);
}
再来看下这里的CSize,MSDN :
CSize(
   int initCX,
   int initCY
)
Parameters
initCX
Sets the cx member for the CSize.
initCY
Sets the cy member for the CSize. 
VC技术内幕:CSize对象可以看成是两个CPoint对象的差
 CDocument类提供了两个函数用于视图类的定位:GetFirstViewPosition()和GetNextView(),具体语法如下:
    virtual POSITION GetFirstViewPosition() const;
    virtual CView* GetNextView(POSITION& rPosition) const;
    注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。
    GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调动的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作):
    CTestView* pTestView;
    POSITION pos=GetFirstViewPosition();
    pTestView=GetNextView(pos);
这样,便可到了CTestView类的指针pTestView.执行完成几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.

但是之几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如:
    pView->IsKindOf(RUNTIME_CLASS(CTestView));
    即可检查pView所指是否是CTestView类。
有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下:
    CView* CTestDoc::GetVieww(CRuntimeClass* pClass)
    {  CView* pView;
        POSITION pos=GetFirstViewPosition();
        while(pos!=NULL)
    {
            pView=GetNextView(pos);
            if(pView->IsKindOf(pClass))
                break;
    }
        if(!pView->IsKindOf(pClass))
            return  NULL;
        return pView;}
    其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:
    1.pos为NULL,即已经不存在下一个视图类供操作;
    2.pView已符合要求。
    3.1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能既是pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一个视图类时就如引。因此需采用两次判断。
    使用该函数应遵循如下格式(以取得CTestView指针为例):
    CTestView* pTestView=(CTestView*)GetView(RUNTIME_CLASS(CTestView));
    RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为CRuntimeClass为指针。
    至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值