MAPPING MODES

Device Context coordinates:
     By default: "Device Units" used
       Video screen: pixels, from upper lefthand corner of screen
          Printer: "printer dots", from upper lefthand corner of page
But pixels and dots not the same size
Output looks different on different devices
Better to use units like millimeters, etc.

Mapping modes--
     Allow app to use "logical units" for text and graphics
     Windows "maps" output to real device ==>
          Output will look similar on all devices
     Mapping Mode is a DC attribute setting
          Can be changed with (Win32API):
          SetMapMode(hDC, mapmode);
          Or (MFC):
          CDC::SetMapMode(mapmode)

MAPPING MODE   LOGICAL UNIT           X-AXIS  Y_AXIS
------------------------------------------------------
MM_TEXT        Pixel(Default)          Right     Dn
MM_HIENGLISH   .001 inch               Right     Up
MM_LOENGLISH   .01 inch                Right     Up
MM_HIMETRIC    .01 mm                  Right     Up
MM_LOMETRIC    .1 mm                   Right     Up
MM_TWIPS       1/20 point=1/1440 inch  Right     Up
MM_ISOTROPIC   Arbitrary (x==y)          Selectable
MM_ANISOTROPIC Arbitrary (x!=y)          Selectable

Windows maps logical coordinates to device coordinates
     Unit: pixel
     +x: toward right, +y" toward bottom

Device coordinate systems:

(1)     Screen Coordinates--
      Used by functions that don't write on client area
          e.g., CreateWindow(), MoveWindow()
       Origin is upper lefthand corner of the screen

(2)     Client-area Coordinates 
     Used with DCs 
     Origin is upper lefthand corner of window client area
     Logical coordinates mapped to these

ClientToScreen() and ScreenToClient()--
     Convert between device and client coordinate systems

Text & Mapping Modes--
     Changing mapping mode changes text location, but not size
     To change size, use a different font and select into DC

Mapping Modes and Messages--
     Device coordinates used for all messages not related to GDI functions   
          e.g., WM_MOVE, WM_SIZE, WM_MOUSEMOVE

Mapping mode only relevant with functions requiring a DC
     e.g., GDI drawing primitives

GetSystemMetrics() and GetDeviceCaps() both use device units
    (TEXTMETRIC structure from GetTextMetrics() uses logical units)

Viewport--
     Rectangular area of screen specified in device coordinates 
          Frequently the client area

Window--
     Rectangular area specified in logical coordinates

Mapping--

     Mathematical transformation that converts a window to a viewport
     Converts logical coordinates (xW,yW) to device coordinates (xV,yV)
     Done by Windows

     xV = (xVExt/xWExt) * (xW - xWOrg) + xVOrg

     yV = (yVExt/yWext) * (yW - yWOrg) + yVOrg

     Point (xW,yW) translated and scaled to (xV,yV)
          x scaling factor = (xVExt/xWext)
          y scaling factor = (yVExt/yWExt)
               These are determined by window/viewport "extents"
               Maintained by Windows
                    Values can be gotten with (Win32API):
                         GetWindowExtEx(hDC,&size) ;
                         GetViewportExtEx(hDC,&size);
                    Or (MFC):
                        CDC::GetWindowExt( );
                        CDC::GetViewportExt( );
                            Both return a CSize object
                    Values can be changed with (Win32 API):
                         SetWindowExtEx(hDC,xWExt,yWExt,&orig_size);
                         SetViewportExtEx(hDC,xVExt,yVExt,&orig_size);
                     Or (MFC):
                         CDC:: SetWindowExt(cx,cy);
                         CDC:: SetViewportExt(cx,cy);

          (xWOrg,yWOrg) = window origin
          (xVOrg,yVOrg) = viewport origin
               (xWOrg,yWOrg) maps to (xVOrg,yVOrg)
               Both are (0,0) in default Device Context

Moving the Origin--

     Changing mapping mode may make output disappear!
     Because logical/physical axes point in the opposite direction
     Could use negative y values 
     But better to move origin 
     Can be done two ways:

1. Win32 API:
SetWindowOrgEx(hDC,x,y,NULL); 
Or (MFC):
CDC::SetWindowOrg(x,y);
     Moves upper lefthand corner of screen/paper up, right by x,y units
     Logical units used

2. Win32 API:
SetVieportOrgEx(hDC,x,y,NULL);
Or (MFC): 
CDC::SetViewportOrg(x,y);
     Moves lower lefthand corner of logical window down, right by x,y units
     Device units (pixels/dots) used

Example--
     Change mapping mode to MM_LOENGLISH
     Move logical origin to bottom left corner of client area
     Draw using those units

CSize size;
pDC = GetDC();
size = pDC->GetWindowExt ();  // Returns x,y in a
                              // CSize structure
                              // Since in MM_TEXT 
                              // mode, "logical" 
                              // units are pixels
pDC->SetViewportOrg (0,size.cy));
        // Move the window down by size.cy pixels
pDC->SetMapMode (MM_LOENGLISH);
        // Now we can draw using 0.01 inch logical
        // coordinates
        // Logical Origin is at lower lefthand 
        // corner of client area

MAPMODE1_MFC  Example Application--

Demonstrates three mappings modes and moving of the origin
Displays rectangle and line of text, using same logical coordinates
     3 different mapping modes used
Logical origin also drawn as a cross in each mapping mode
Note different position/size of rectangle/text

User clicks a mapping mode from menu 
WM_COMMAND processing:
     Sets nMapMode variable to appropriate value
     Forces repaint of client area calling Invalidate( )

Resulting WM_PAINT, OnDraw( ) processing:
     Calls SetMapMode( ) to set mapping mode according to value nMapMode
     Calls SetViewportOrg( ) to move window down/right by 100 pixels
     Calls MoveTo() and LineTo( ) to draw cross at logical origin 
     Calls Rectangle( ) to draw a rectangle at log coords (50,50), (200,100)
     Calls TextOut( ) to display string  at log coords (50,50) 


MM_ISOTROPIC, MM_ANISOTROPIC Mapping Modes--

Coordinate axes can use any unit size
x-axis can point left or right
the y-axis can point either up or down

MM_ISOTROPIC--x and y units must be same size

MM_ANISOTROPIC--Different sizes are used for x and y units

Determine  units to be used by setting X and Y scaling factors with calls to:
     SetWindowExtEx ( xWExt, yWExt);
     SetViewportExtEx ( xVExt, yVExt);
     Or:
     CDC:: SetWindowExt(xWExt, yWExt);
     CDC:: SetViewportExt(xVExt, yVExt);
          X scaling factor = xVExt/xWExt
          Y scaling factor = yVExt/yWExt
          Used in mapping equations
          To reverse either axis, set one Ext value to a negative number

Example 1: Coordinate  system where each logical unit is two pixels--

pDC = GetDC ( );
pDC->SetMapMode (MM_ISOTROPIC);
pDC->SetWindowExt (1, 1);   
pDC->SetViewportExt (2, 2);  // x-factor = y-factor = 2/1

Example 2--Logical coordinate system with y-axis up,
                    y-unit = 1/4 pixel, x-axis is to be unchanged--

pDC = GetDC ( );
pDC->SetMapMode (MM_ANISOTROPIC);
pDC->SetWindowExt (1, -4);  
pDC->SetViewportExt (1, 1);  // x-factor=1/1) ,  y-factor=1/(-4)

Example 3--Coordinate system where:
                    client area always 1000 units high and wide
                    y-axis points up

CSize size;
pDC = GetDC ( );
size=pDC->GetWindowExt (); // Get client area size 
       Returns size in default MM_TEXT Mapping Mode units, here pixels
pDC->SetMapMode (MM_ANISOTROPIC);
pDC->SetWindowExt (1000, -1000);      // x-factor =  size.cx/1000
pDC->SetViewportExt (size.cx, size.cy);  // y-factor = size.cy/(-1000)

Mapping equations ==>
     x=1000 --> size.cx
          corresponds to right edge of the client area
     y=1000 --> -size.cy
          would correspond to upper of client area if y-axis were reversed
Independent of how the window resized.

MAPMODE2_MFC Example Application--

Draws rectangle and ellipse that always fit exactly inside client area

User changes size of window (WM_SIZE message) ==>
     OnSize(cx,cy) --> cx,cy extent of window client area in device units 
     Save in variables nX and nY
Window exposed --> WM_PAINT message, OnDraw() ==>
     Set mapping mode to MM_ANISOTROPIC with pDC->SetMapMode()
     Set scaling factors to 100/nX & 100/nY with:
          pDC->SetViewportExtEx() using xWExt=nX and yWext=nY
          pDC->SetWindowExtEx() using xVExt=100 and yVExt=100 
             ==>logical coordinate system always scaled to 100 x 100 logical units 
     Draw ellipse with pDC->Ellipse(0, 0, 100, 100) ==>
          After mapping, ellipse will lie inside rectangle (0,0)-(nX,nY)
             (just fits inside the client area)
     Draw rectangle with pDC->Rectangle(20, 20, 80, 80) ==>
          After mapping becomes (0.2*nX,0.2*nY)-(0.8*nX,0.8*nY)
               (borders 1/5 of way into the window)
     y-axis still points downward ==>
          don't have to move the origin