When drawing on Microsoft Windows, the coordinates of
the drawing area are located on the upper-left corner of the screen.
Everything positioned on the screen takes its reference on that point.
That point can be illustrated in a Cartesian coordinate system as (0,0)
where the horizontal axis moves from (0,0) to the right and the vertical
axis moves from (0,0) down:

This
starting origin is only the default coordinate system of the operating
system. Therefore, if you draw a shape with the following call, Ellipse(-100,
-100, 100, 100), you would get a circle whose center is positioned on
the top-left corner of the screen. In this case, only the lower-right 3/4
of the circle would be seen:

In the same way, you can draw any geometric or
non-geometric figure you want, using one of the CPaintDC methods or
creating methods of your choice. For example, the following code draws a
vertical and a horizontal lines that cross each other in the center middle
of the form:

As seen above, the default coordinate system has its
origin set on the top-left section of the screen. The horizontal axis
moves positively from the origin to the right direction. The vertical axis
moves from the origin to the bottom direction. To illustrate this, let's
draw a circle with a radius whose center is at the origin (0, 0) with a
radius of 50 units. Let's also draw a line from the origin (0, 0) to
(100, 100):

This default origin is fine for most, if not all
regular, operations performed on graphics applications. For example, most
graphics application, including Paint Shop Pro use this origin. Sometimes,
you will need to control the position of the origin of the coordinate
system. For example, most CAD applications, including AutoCAD, allow the
user to set this origin.

The MFC provides various functions to deal with the
coordinates positions and extents of the drawing area, including functions
used to set the origin of the coordinate system anywhere you want on the
screen. Since you are drawing on a device context, all you need to do is
simply call the CDC::SetViewportOrg() method. It is overloaded with
two versions, which allow you to use either the X and the Y coordinates or
a defined point. The syntaxes of this method are:

SetViewportOrg(int X, int Y);

SetViewportOrg(CPoint Pt);

When calling this function, simply specify where you
want the new origin to be. If using the second version, the argument can
be a Win32 POINT structure or an MFC TPoint class. To see the effect of
this function, let's move the origin 200 units in the positive direction
of the X axis and 150 units in the positive direction of the vertical axis
without changing the circle and the line. Our OnPaint event would look
like this:

As seen already, the SetViewportOrg() method can be
used to change the origin of the device context. It also uses an
orientation of axes so that the horizontal axis moves positively from
(0, 0) to the right. The vertical axis moves positively from (0, 0)
down.

As you can see, our line is not at 45º. Instead of
being in the first quadrant, it is in the fourth. This is due to the
default orientation of the coordinate system.

The Map Modes

To control the orientation of the axes of the device
context, you use a method of the CDC class called SetMapMode(). Its
syntax is:

int SetMapMode(int nMapMode);

As you are about to see, this method can be used to
do two things, depending on the value of the argument. It can control the orientation of the coordinate system you want to use for your
application. It also helps with the unit system you would prefer to use.

The argument of this method is a constant integer
that species the mapping mode used. The possible values are MM_TEXT, MM_LOENGLISH, MM_HIENGLISH,
MM_ANISOTROPIC, MM_HIMETRIC, MM_ISOTROPIC, MM_LOMETRIC,
and MM_TWIPS.

The default map mode used is the MM_TEXT. In
other words, if you don't specify another, this is the one your
application would use. With this map mode, the dimensions or measurements
you specify in your CDC methods are respected and kept "as is".
Also, the axes are oriented so the horizontal axis moves from (0, 0) to
the right and the vertical axis moves from (0, 0) down. For example, the
above OnPaint event can be re-written as follows and would produce the
same result:

The MM_LOENGLISH, like some of the other map
modes (excluding MM_TEXT as seen above), performs two actions. It
changes the orientation of the vertical axis: the positive y axis would move from
(0, 0) up:

Also, each unit of measure is multiplied by 0.01 inch, which
means each unit you provide is divided by 100 (unit/100). This also means
that the units are reduced from their stated measures by a 100th. Observe the effect
of the MM_LOENGLISH map mode on the above OnPaint() event :

As you can see, now the lines are drawn respecting the
positive and the negative orientations of the axes, fulfilling the
requirements of a Cartesian coordinate system. At the same time, the
lengths we used have been reduced: the circle is smaller and the lines are
shorter.

Like the MM_LOENGLISH map mode, the MM_HIENGLISH
sets the orientation so
the vertical axis moves from (0, 0) up. Unlike the MM_LOENGLISH,
the MM_HIENGLISH map mode reduces each unit by a factor of 0.001
inch. This means that each unit is divided by 1000 (1/1000 = 1000th)
which is significant and can change the display of a drawing. Here is
its effect:

Notice that we are still using the same dimensions for
our lines and circle.

The MM_LOMETRIC map mode uses the same axes
orientation as the previous two modes. By contrast, the MM_LOMETRIC
multiplies each unit by 0.1 millimeter. This means that each unit is
reduced by 10%. Here is an example:

The MM_TWIPS map mode divides each logical unit by 20.
Actually a twip is equivalent to 1/1440 inch. Besides this unit
conversion, the axes are oriented so the horizontal axis moves from the
origin (0, 0) to the right while the vertical axis moves from the origin
(0, 0) up. Here is an example:

The map modes we have used so far
allowed us to select the orientation of the axes, especially the y axis.
Furthermore, we couldn't influence any conversion unit for the dimensions
we specified on our drawings. This is because each one of these mapping
modes (MM_TEXT, MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC,
MM_LOMETRIC, and MM_TWIPS) has a fixed set of attributes such as the orientation of its
axes and the conversion used on the provided dimensions. What if you want to
control the orientation of axes and/or the conversion applied on the
dimensions you provide in your drawing (have you ever used AutoCAD?).

Consider the following OnPaint() event. It draws a 200x200
pixels square with a red border and an aqua background. The square starts
at 100x100 pixels on the negative sides of both axes and it
continues 100x100 pixels on the positive sides of both axes.
For better illustration, the event also draws a diagonal line at 45º
starting at the origin (0, 0):

As you can see, we get only the the
lower-right 3/4 portion of the square and the line is pointing in the 3 to
6 quadrant of a clock .

Imagine that you want the origin (0, 0) to be
positioned in the center middle of the form, or to be more precise, to
position the origin at (340, 220).
We saw already that you could use the CDC::SetViewportOrg() method
(keep in
mind that this method only changes the origin of the coordinate system;
it doesn't influence the orientation of axes nor does it control the units
or dimensions) to specify
the origin. Here is an example (we are not specifying the map mode because
MM_TEXT can be used for us as the default):

To control your own unit system, the
orientation of the axes or how the application converts the units used on
your application, use either the MM_ISOTROPIC or the MM_ANISOTROPIC map
modes. The first thing you should do is to call the CDC::SetMapMode()
method and specify one of these two constants (either MM_ISOTROPIC or MM_ANISOTROPIC). Here is an example:

Don't rely on the above picture, after
calling the CDC::SetMapMode() method with MM_ISOTROPIC (or
MM_ANISOTROPIC) as
argument, you are not supposed to stop there. The purpose of these two map
modes is to let you control the orientation of the axes and the conversion
of the units.

The difference between both map modes is that, when using
the MM_ISOTROPIC map mode, one unit in the horizontal axis is
equivalent to one unit in the vertical axis. This is not the case for the MM_ANISOTROPIC
map mode which allows you to control however the units should be converted
on each individual axis.

Therefore, after calling SetMapMode() and specifying the
MM_ISOTROPIC (or MM_ANISOTROPIC), you must call the CDC:SetWindowExt()
method. This method specifies how much each new unit will be multiplied by the old or default
unit system. The CDC::SetWindowExt() method comes in two versions
with the following syntaxes:

CSize SetWindowExt(int cx, int cy);
CSize SetWindowExt(SIZE size);

If using the first version, the first argument to this
method, cx, specifies the logical conversion multiplier used for each unit on the horizontal axis. The
second argument, cy,
specifies the logical conversion multiplier used for each unit on the vertical axis.

The second version of the method can be used if you
know the desired logical width and height as a SIZE object. Here is an example:

After calling the SetWindowExt() function,
you should call the SetViewportExt() function. Its job is to
specify the horizontal and vertical units of the device context being
used. It comes in two flavors with the following syntaxes: