so true

          心懷未來,開創(chuàng)未來!
          隨筆 - 160, 文章 - 0, 評論 - 40, 引用 - 0
          數(shù)據(jù)加載中……

          坐標(biāo)空間

          Win32應(yīng)用程序設(shè)計接口(API)使用四種坐標(biāo)空間:世界坐標(biāo)系空間、頁面空間、設(shè)備空間、和物理設(shè)備空間。應(yīng)用程序運用世界坐標(biāo)系空間對圖形輸出進行旋轉(zhuǎn)、斜切或者反射(一般坐標(biāo)系完不成這些特殊的功能)。
          Win32 API把世界坐標(biāo)系空間和頁面空間稱為邏輯空間;最后一種坐標(biāo)空間(即物理設(shè)備空間)通常指應(yīng)用程序窗口的客戶區(qū);但是它也包括整個桌面、完整的窗口(包括框架、標(biāo)題欄和菜單欄)或打印機的一頁或繪圖儀的一頁紙。
          如果該應(yīng)用程序調(diào)用了SetWorldTransform函數(shù),那么映射就從應(yīng)用程序的世界坐標(biāo)系空間開始;否則,映射在頁面空間中進行。
          下面有一個從MSDN中拷貝出來的例子如下:
          enum TransformStyle { SCALE, TRANSLATE, ROTATE, SHEAR, REFLECT, NORMAL};

          void CXXXXView::TransformAndDraw(int iTransform, HWND hWnd)
          {
              HDC hDC;
              XFORM xForm;
              RECT rect;
           
              // Retrieve a DC handle for the application's window.
           
              hDC = ::GetDC(hWnd);
           
              // Set the mapping mode to LOENGLISH. This moves the
              // client area origin from the upper left corner of the
              // window to the lower left corner (this also reorients
              // the y-axis so that drawing operations occur in a true
              // Cartesian space). It guarantees portability so that
              // the object drawn retains its dimensions on any display.

              SetGraphicsMode(hDC, GM_ADVANCED);
              SetMapMode(hDC, MM_LOENGLISH);
           
              // Set the appropriate world transformation (based on the
              // user's menu selection).
           
              switch (iTransform)
              {
                  case SCALE:        // Scale to 1/2 of the original size.
                      xForm.eM11 = (FLOAT) 0.5;
                      xForm.eM12 = (FLOAT) 0.0;
                      xForm.eM21 = (FLOAT) 0.0;
                      xForm.eM22 = (FLOAT) 0.5;
                      xForm.eDx  = (FLOAT) 0.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
           
                  case TRANSLATE:   // Translate right by 3/4 inch.
                      xForm.eM11 = (FLOAT) 1.0;
                      xForm.eM12 = (FLOAT) 0.0;
                      xForm.eM21 = (FLOAT) 0.0;
                      xForm.eM22 = (FLOAT) 1.0;
                      xForm.eDx  = (FLOAT) 75.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
           
                  case ROTATE:      // Rotate 30 degrees counterclockwise.
                      xForm.eM11 = (FLOAT) 0.8660;
                      xForm.eM12 = (FLOAT) 0.5000;
                      xForm.eM21 = (FLOAT) -0.5000;
                      xForm.eM22 = (FLOAT) 0.8660;
                      xForm.eDx  = (FLOAT) 0.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
           
                  case SHEAR:       // Shear along the x-axis with a
                                    // proportionality constant of 1.0.
                      xForm.eM11 = (FLOAT) 1.0;
                      xForm.eM12 = (FLOAT) 1.0;
                      xForm.eM21 = (FLOAT) 0.0;
                      xForm.eM22 = (FLOAT) 1.0;
                      xForm.eDx  = (FLOAT) 0.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
           
                  case REFLECT:     // Reflect about a horizontal axis.
                      xForm.eM11 = (FLOAT) 1.0;
                      xForm.eM12 = (FLOAT) 0.0;
                      xForm.eM21 = (FLOAT) 0.0;
                      xForm.eM22 = (FLOAT) -1.0;
                      xForm.eDx  = (FLOAT) 0.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
           
                  case NORMAL:      // Set the unity transformation.
                      xForm.eM11 = (FLOAT) 1.0;
                      xForm.eM12 = (FLOAT) 0.0;
                      xForm.eM21 = (FLOAT) 0.0;
                      xForm.eM22 = (FLOAT) 1.0;
                      xForm.eDx  = (FLOAT) 0.0;
                      xForm.eDy  = (FLOAT) 0.0;
                      SetWorldTransform(hDC, &xForm);
                      break;
            default:
             break;
              }
           
              // Find the midpoint of the client area.
           
              ::GetClientRect(hWnd, (LPRECT) &rect);
              DPtoLP(hDC, (LPPOINT) &rect, 2);
           
              // Select a hollow brush.
           
              SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
           
              // Draw the exterior circle.
           
              Ellipse(hDC, (rect.right / 2 - 100), (rect.bottom / 2 + 100),
                  (rect.right / 2 + 100), (rect.bottom / 2 - 100));
           
              // Draw the interior circle.
           
              Ellipse(hDC, (rect.right / 2 -94), (rect.bottom / 2 + 94),
                  (rect.right / 2 + 94), (rect.bottom / 2 - 94));
           
              // Draw the key.
           
              Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 113),
                  (rect.right / 2 + 13), (rect.bottom / 2 + 50));
              Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 96),
                  (rect.right / 2 + 13), (rect.bottom / 2 + 50));
           
              // Draw the horizontal lines.
           
              MoveToEx(hDC, (rect.right/2 - 150), (rect.bottom / 2 + 0), NULL);
              LineTo(hDC, (rect.right / 2 - 16), (rect.bottom / 2 + 0));
           
              MoveToEx(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 0), NULL);
              LineTo(hDC, (rect.right / 2 + 13), (rect.bottom / 2 + 0));
           
              MoveToEx(hDC, (rect.right / 2 + 16), (rect.bottom / 2 + 0), NULL);
              LineTo(hDC, (rect.right / 2 + 150), (rect.bottom / 2 + 0));
           
              // Draw the vertical lines.
           
              MoveToEx(hDC, (rect.right/2 + 0), (rect.bottom / 2 - 150), NULL);
              LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 16));
           
              MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 13), NULL);
              LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 13));
           
              MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 16), NULL);
              LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 150));
           
              ::ReleaseDC(hWnd, hDC);
          }
          頁面空間到設(shè)備空間的轉(zhuǎn)換是原Windows接口的一部分,也是我們可以用代碼控制的一種轉(zhuǎn)換,頁面空間到設(shè)備空間的轉(zhuǎn)換所用的是兩個矩形的寬與高的比率,其中頁面空間中的矩形被稱為窗口,設(shè)備空間中的矩形被稱為視口,Windows把窗口原點映射到視口原點,把窗口范圍映射到視口范圍,就完成了這種轉(zhuǎn)換。

          設(shè)備空間到物理空間的轉(zhuǎn)換有幾個獨特之處:它只限于平移,并由Windows的窗口管理部分控制,這種轉(zhuǎn)換的唯一用途是確保設(shè)備空間的原點被映射到物理設(shè)備上的適當(dāng)點上。沒有函數(shù)能設(shè)置這種轉(zhuǎn)換,也沒有函數(shù)可以獲取有關(guān)數(shù)據(jù)。因此我們不能人為干預(yù)。

          實際上我們需要完成的是從頁面空間到物理設(shè)備空間的映射,由于物理設(shè)備空間通常指應(yīng)用程序窗口的客戶區(qū),因此我們只用關(guān)注于從頁面空間到設(shè)備空間的映射就可以了。

          一旦應(yīng)用程序建立了設(shè)備描述表(DC),并立即開始調(diào)用GDI繪圖或輸出函數(shù),則運用默認頁面空間到設(shè)備空間的轉(zhuǎn)換和設(shè)備空間到客戶區(qū)的轉(zhuǎn)換(在應(yīng)用程序調(diào)用SetWorldTransform函數(shù)之前,不會出現(xiàn)世界坐標(biāo)空間到頁面空間的轉(zhuǎn)換)。

          默認頁面空間到設(shè)備空間的轉(zhuǎn)換結(jié)果是一對一的映射;即頁面空間上給出的一點映射到設(shè)備空間的一個點。正如前文講到的,這種轉(zhuǎn)換沒有以矩陣指定,而是通過把視口寬除以窗口寬,把視口高除以窗口高而得出的。在默認的情況下,視口尺寸為1x1個象素,窗口尺寸為1x1頁單位。

          幾乎在所有的GDI函數(shù)中使用的坐標(biāo)值都是采用的邏輯單位。Windows必須將邏輯單位轉(zhuǎn)換為“設(shè)備單位”,即像素。Windows對所有的消息(如WM_SIZE、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP),所有的非GDI函數(shù)和一些GDI函數(shù)(例如GetDeviceCaps函數(shù)),永遠使用設(shè)備坐標(biāo)。“窗口”是基于邏輯坐標(biāo)的,邏輯坐標(biāo)可以是象素、毫米、英寸等單位;“視口”是基于設(shè)備坐標(biāo)(象素)的。通常,視口和客戶區(qū)是相同的。

          窗口(邏輯)坐標(biāo)轉(zhuǎn)換為視口(設(shè)備)坐標(biāo)的兩個公式:
          xViewport=(xWindow-xWinOrg)*xViewExt/xWinExt+xViewOrg
          yViewport=(yWindow-yWinOrg)*yViewExt/yWinExt+yViewOrg

          視口(設(shè)備)坐標(biāo)轉(zhuǎn)換為窗口(邏輯)坐標(biāo)的兩個公式:
          xWindow=(xViewPort-xViewOrg)*xWinExt/xViewExt+xWinOrg
          yWindow=(yViewPort-yViewOrg)*yWinExt/yViewExt+yWinOrg

          在MM_TEXT映射方式下邏輯坐標(biāo)和設(shè)備坐標(biāo)的相互轉(zhuǎn)換
          窗口(邏輯)坐標(biāo)轉(zhuǎn)換為視口(設(shè)備)坐標(biāo)的兩個公式:
          xViewport = xWindow-xWinOrg+xViewOrg
          yViewport = yWindow-yWinOrg+yViewOrg
          視口(設(shè)備)坐標(biāo)轉(zhuǎn)換為窗口(邏輯)坐標(biāo)的兩個公式:
          xWindow = xViewport-xViewOrg+xWinOrg
          yWindow = yViewport-yViewOrg+yWinOrg

          CDC中提供了兩個成員函數(shù)函數(shù)SetViewportOrg和SetWindowOrg,用來改變視口和窗口的原點。
          如果將視口原點設(shè)置為(xViewOrg,yViewOrg),則邏輯點(0,0)就會被映射為設(shè)備點(xViewOrg,yViewOrg)。如果將窗口原點改變?yōu)?xWinOrg,yWinOrg),則邏輯點(xWinOrg,yWinOrg)將會被映射為設(shè)備點(0,0),即左上角。
          不管對窗口和視口原點作什么改變,設(shè)備點(0,0)始終是客戶區(qū)的左上角。

          說一點自己的感悟:
          坐標(biāo)系有四種,上面介紹的很詳細了,就不多說了。
          對于很重要的從頁面坐標(biāo)到設(shè)備坐標(biāo)的轉(zhuǎn)換,頁面坐標(biāo)用邏輯單位表示,可以采用長度度量單位如米或英尺來與邏輯單位相掛鉤,也可以用像素來和邏輯單位掛鉤,而且我們說窗口;設(shè)備坐標(biāo)只用像素表示,而且我們說視口。因此從邏輯坐標(biāo)到設(shè)備坐標(biāo)有很多種映射方式:
          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
          還有MM_ISOTROPIC(長度和寬度的比例因子一致)和MM_ANISOTROPIC(長寬比例因子可以不一致)。
          舉個例子來說,比如MM_HIMETRIC,每個邏輯單元代表0.01毫米,比如邏輯橫坐標(biāo)100,代表1毫米的長度,而對于設(shè)備坐標(biāo)(也可以說物理坐標(biāo))來說,如果分辨率是96dpi,那么也就是1英尺有96個像素,因此從1毫米先轉(zhuǎn)換為多少英尺,然后再轉(zhuǎn)換為多少個像素,就可以知道屏幕上在哪個位置顯示這個位置了。

          還需要注意的是,GDI函數(shù)都是用邏輯坐標(biāo)來計算的,而非GDI函數(shù)以及少部分GDI函數(shù)都是用設(shè)備坐標(biāo)也就是像素來表示的,設(shè)備坐標(biāo)只能用像素來計算。

           

           

          posted on 2008-02-23 19:18 so true 閱讀(747) 評論(0)  編輯  收藏 所屬分類: C&C++

          主站蜘蛛池模板: 宣汉县| 天等县| 福鼎市| 清原| 体育| 万山特区| 随州市| 赤峰市| 东方市| 岫岩| 宁河县| 西平县| 仁怀市| 齐河县| 余庆县| 仁化县| 盐源县| 贡嘎县| 汶川县| 新安县| 腾冲县| 贵港市| 神农架林区| 和静县| 曲靖市| 盐城市| 读书| 黔江区| 连城县| 浪卡子县| 石首市| 平乡县| 米易县| 富蕴县| 隆尧县| 汉川市| 保靖县| 贡山| 安义县| 渝中区| 正安县|