The "Metric" Mapping Modes

Windows includes five mapping modes that express logical coordinates in physical measurements. Because logical coordinates on the x-axis and y-axis are mapped to identical physical units, these mapping modes help you to draw round circles and square squares.

The five ”metric“ mapping modes are arranged below in order of lowest precision to highest precision. The two columns at the right show the size of the logical units in terms of inches (in.) and millimeters (mm) for comparison:

Mapping Mode Logical Unit Inch Millimeter

MM_LOENGLISH 0.01 in. 0.01 0.254
MM_LOMETRIC 0.1 mm 0.00394 0.1
MM_HIENGLISH 0.001 in. 0.001 0.0254
MM_TWIPS* 1/1440 in. 0.000694 0.0176
MM_HIMETRIC 0.01 mm 0.000394 0.01

* A twip equals 1/20 of a point, which itself equals 1/72 inch.

To give you an idea of how the MM_TEXT mode fits in with these resolutions, on a standard EGA display each pixel is 0.375 mm wide and 0.5 mm tall, so EGA device coordinates are coarser than the logical coordinates for any of the metric mapping modes.

On a 300-dots-per-inch laser printer, each pixel is 0.0033 inch—a higher resolution than MM_LOENGLISH and MM_LOMETRIC but not as high as MM_HIENGLISH, MM_TWIPS, or MM_HIMETRIC.

The default origins and extents are shown below:

Window origin: (0, 0) Can be changed
Viewport origin: (0, 0) Can be changed
Window extent: (?, ?) Cannot be changed
Viewport extent: (?, ?) Cannot be changed

The window and viewport extents depend on the mapping mode and the aspect ratio of the device. As I mentioned earlier, the extents aren't important by themselves but take on meaning only when expressed as ratios. Here are the translation formulas again:

xViewExt
xViewport = (xWindow - xWinOrg) * -------- + xViewOrg
xWinExt

yViewExt
yViewport = (yWindow - yWinOrg) * -------- + yViewOrg
yWinExt

For MM_LOENGLISH, for instance, Windows calculates the extents to be the following:

xViewExt
-------- = number of horizontal pixels in 0.01 in.
xWinExt

yViewExt
--------- = number of vertical pixels in 0.01 in.
yWinExt

For many display devices (such as the EGA), this ratio will be less than 1. Because Windows works entirely with integers, the use of a ratio rather than an absolute scaling factor is necessary to reduce loss of precision when converting logical and device coordinates.

Notice the negative sign in front of the ratio of extents for the vertical axis. This negative sign changes the orientation of the y-axis. For these five mapping modes, y values increase as you move up the device. The default window and viewport origins are (0, 0). This fact has an interesting implication. When you first change to one of these five mapping modes, the coordinate system looks like this:

The only way you can display anything in the client area is to use negative values of y. For instance, this code:

SetMapMode (hdc, MM_LOENGLISH) ;

TextOut (hdc, 100, -100, "Hello", 5) ;

displays Hello 1 inch from the top and left edges of the client area.

To preserve your sanity, you'll probably want to avoid this. One solution is to set the logical (0, 0) point to be the lower left corner of the client area. Assuming that cyClient is the height of the client area in pixels, you can do this by calling SetViewportOrg:

SetViewportOrg (hdc, 0, cyClient) ;

Now the coordinate system looks like this:

Alternatively, you can set the logical (0, 0) point to the center of the client area:

SetViewportOrg (hdc, cxClient / 2, cyClient / 2) ;

The coordinate system looks like this:

Now we have a real four-quadrant Cartesian coordinate system with equal logical units on the x-axis and y-axis in terms of inches, millimeters, or twips.

You can also use the SetWindowOrg function to change the logical (0, 0) point, but the task is a little more difficult because the parameters to SetWindowOrg have to be in logical coordinates. You would first need to convert cyClient to a logical coordinate using the DPtoLP function. Assuming that the variable pt is a structure of type POINT, this code changes the logical (0, 0) point to the center of the client area:

pt.x = cxClient ;

pt.y = cyClient ;

DPtoLP (hdc, &pt, 1) ;

SetWindowOrg (hdc, -pt.x, -pt.y) ;