现在的位置: 首页 > 综合 > 正文

设备坐标和逻辑坐标

2012年09月22日 ⁄ 综合 ⁄ 共 5104字 ⁄ 字号 评论关闭

要用到两个函数:

CDC::LPtoDP  将逻辑坐标转换为设备坐标

CDC::DPtoLP  将设备坐标转换为逻辑坐标

 

设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。

  逻辑坐标(Logical Coordinate)是系统用作记录的坐标。又称文档坐标。在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:

  1. 窗口为非滚动窗口
  2. 窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。

  在VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用CDC 函数DPtoLP()将设备坐标转化为逻辑坐标,同样可以用LPtoDP()将逻辑坐标转化为设备坐标。

  
ScreenToClient和ClientToScreen实际上是转换一个参照物的概念,如ie客户区上一个button,相对于ie的坐标是(x, y),ie客户区相对于屏幕原点的坐标是(x0 , y0),那么button的screen坐标就是(x+x0, y+y0) 。ScreenToClient和ClientToScreen都假定坐标是设备坐标。

在EX05C中(EX05C是一个例子程序,只需看函数中的代码即可):

我们现在来看看逻辑坐标和物理坐标是怎么转换的。

void CEX05CView::OnInitialUpdate()

{

       CScrollView::OnInitialUpdate();

 

       // TODO: calculate the total size of this view

       CSize sizeTotal(800, 1050);

       CSize sizePage(sizeTotal.cx/2, sizeTotal.cy/2);

       CSize sizeLine(sizeTotal.cx/50, sizeTotal.cy/50);

       SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);

}

上面程序中的SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);制定了映射模式为

MM_LOENGLISH,整个客户端逻辑区域为800 x 1050逻辑单位(sizeTotal);横向翻页的大小为400逻辑单位,纵向翻页的大小为525逻辑单位(sizePage); 横向一列的大小为800 / 50 = 16逻辑单位,纵向一行的大小为1050 / 50 = 21逻辑单位。

在MM_LOENGLISH映射模式下,每逻辑单位是0.01英寸。

void CEX05CView::OnLButtonDown(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

       CRect rectEllipse(m_pointTopLeft, m_sizeEllipse);

       CRgn circle;

 

       CClientDC dc(this);

       OnPrepareDC(&dc);

//     TRACE("Check Point3: HORZSIZE = %d, VERTSIZE = %d/n",dc.GetDeviceCaps(HORZSIZE), dc.GetDeviceCaps(VERTSIZE));

//     HORZSIZE = 320mm, VERTSIZE = 240mm

       TRACE("/nBefore LPtoDP:/n");

       TRACE("rectEllipse.top = %d, rectEllipse.bottom = %d, rectEllipse.left = %d, rectEllipse.right = %d/n",

              rectEllipse.top, rectEllipse.bottom, rectEllipse.left, rectEllipse.right);

       dc.LPtoDP(rectEllipse);

       TRACE("/nAfter LPtoDP:/n");

       TRACE("rectEllipse.top = %d, rectEllipse.bottom = %d, rectEllipse.left = %d, rectEllipse.right = %d/n",

              rectEllipse.top, rectEllipse.bottom, rectEllipse.left, rectEllipse.right);

//     LPtoDP将rectEllipse从逻辑坐标转换成设备坐标

       circle.CreateEllipticRgnIndirect(rectEllipse);

//     TRACE("Check Point2: point = (%d, %d)/n", point.x, point.y);

//  point为物理设备坐标

       if(circle.PtInRegion(point))

       {

              SetCapture();

//            Causes all subsequent mouse input to be sent to the current CWnd object regardless of the

//            position of the cursor.

              m_bCaptured = TRUE;

              CPoint pointTopLeft(m_pointTopLeft);

              dc.LPtoDP(&pointTopLeft);

              m_sizeOffset = point - pointTopLeft;

//            m_sizeOffset, point, pointTopLeft皆为设备坐标

              ::SetCursor(::LoadCursor(NULL, IDC_CROSS));

       }

       CScrollView::OnLButtonDown(nFlags, point);

}

我们来看看rectEllipse在语句dc.LPtoDP(rectEllipse);前后的变化:

Before LPtoDP:

EX05C: rectEllipse.top = -219, rectEllipse.bottom = -319, rectEllipse.left = 199, rectEllipse.right = 299

After LPtoDP:

EX05C: rectEllipse.top = 178, rectEllipse.bottom = 259, rectEllipse.left = 162, rectEllipse.right = 243

 

-219(逻辑坐标)是怎样转换为178(设备坐标)的呢?

设备坐标:屏幕的左上方为(0, 0),屏幕的右边为x坐标的正方向,屏幕的下边为y轴的正方向。

逻辑坐标:屏幕的左上方为(0, 0),右边为x坐标的正方向,对于不同的映射模式,y轴的正方向是不一样的,对

于MM_LOENGLISH而言,向上的方向是正方向,向下的方向是负方向。

在MM_LOENGLISH映射模式下,219个逻辑单位的长度为:

219 * 0.01 = 2.19英寸 = 2.19 * 2.54 = 5.5626 厘米

在上面程序中的CheckPoint3中,我们可以得到屏幕的物理尺寸为:320毫米 x 240毫米,另外补充说明一下,本电脑的屏幕分辨率是1024 * 768。因此,

(5.5626 / 24) * 768 = 178.0032像数

以上就是-219(逻辑坐标)转换为178(设备坐标)的详解。其他几个坐标也是如此转换而来。

 

现在我们看看卷动右边的滚动条的情况下,坐标是怎样变化的。

先将滚动条往下滚动两行,然后随便将椭圆拖放到一个位置,如下图所示:

 

TRACE语句的输出结果为:

Before LPtoDP:

EX05C: rectEllipse.top = -469, rectEllipse.bottom = -569, rectEllipse.left = 801, rectEllipse.right = 901

After LPtoDP:

EX05C: rectEllipse.top = 347, rectEllipse.bottom = 428, rectEllipse.left = 651, rectEllipse.right = 732

 

逻辑坐标-469是怎样转换成设备坐标347的呢?

在不考虑卷动的情况下,逻辑坐标-469应该转换成的设备坐标为:

(469 * 0.01 * 2.54 / 24) * 768 = 381.2032像数 = 381像数。

由于在垂直方向向下滚动了两行即 (1050 / 50) * 2 = 42逻辑单位,转换成设备坐标为:

(42 * 0.0.1 * 2.54 / 24) * 768 = 34.1736像数 = 34像数。

因此,考虑到卷动了2行的情况,逻辑坐标-469转换成设备坐标应该是:

381 - 34 = 347像数

这就是在有卷动的情况下,逻辑坐标和设备坐标转换的详细说明。

1. void CEX05CView::OnLButtonDown(UINT nFlags, CPoint point)中的point是设备坐标,而非逻辑坐标。如果要该函数中画图,就一定要将point转换成逻辑坐标。

2. InvalidateRect(rectOld, TRUE);中的rectOld是设备坐标,而非逻辑坐标。

3. pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse)); m_pointTopLeft, m_sizeEllipse皆为逻辑坐标。

画图时,皆用逻辑坐标。InvalidateRect时或者判断某点是否位于某个区域之中时皆用设备坐标。

 

 

void CEX05CView::OnDraw(CDC* pDC)

{

       CEX05CDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

 

       CBrush brushHatch(HS_DIAGCROSS, RGB(255, 0, 0));

       CPoint point(0, 0);

 

       pDC->LPtoDP(&point);

       pDC->SetBrushOrg(point);

 

       pDC->SelectObject(&brushHatch);

       TRACE("Check Point1: m_pointTopLeft = %d, m_sizeEllipse = %d/n", m_pointTopLeft, m_sizeEllipse);

//     m_pointTopLeft, m_sizeEllipse皆为逻辑坐标。画图时,皆用逻辑坐标,InvalidateRect时皆用设备坐标。

       pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse));

       pDC->SelectStockObject(BLACK_BRUSH);

       pDC->Rectangle(CRect(100, -100, 200, -200));

}

没有上面下划线语句的情况下,椭圆中的填充部分,在屏幕卷动时会不正常(右边滚动条不卷动时是正常的):

 

 

有上面下划线语句的情况下,不管是否卷动,椭圆中的填充部分都是正常的。如下图:

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pathuang68/archive/2009/05/27/4219657.aspx

抱歉!评论已关闭.