Navigation Bar

13 August 2013

[ MFC ] - Mapping Modes in VC++ Explained with SetWindowOrg, SetViewPortOrg

1. Introduction


MFC supports two kinds of coordinate systems. One is Device coordinate and the other one is Logical Co-Ordinate. In device coordinate, we specify everything in terms of pixels. In logical coordinate, we measure each unit in terms of Metric standard or British standard. How each unit maps to the logical measure is called the mapping. We can specify the mapping using the mapping modes.

In this article, I will walk you through the examples and explanation videos, which will help you in understanding the Mapping modes and performing the drawing using the device contexts. I will also help in understanding the ViewPort Origin and Window Origin.


2. Create SDI Application


The first step is creating the SDI Application without any document view Architecture support. Once the application is created, you can start your drawing using the OnPaint() handler. OK, Have a look at the below picture:



In the above depiction, the black screen is the desktop (Hided the Icons) and top left corner of the desktop (shown as P1) is the origin of the screen coordinate. The notepad application is displayed on top of the desktop(Screen) area. Point P2 specifies the origin of the notepad window or we can say that P2 is the origin of the window coordinate system. If there are three windows in the desktop area, then three such window coordinate exits (one for each window). The client area of the window is nothing but the working area of the window. In the case of the notepad, the actual working area is the portion of the window in which we actually type our textual content. Point P3 specifies the Origin of the client coordinate system. In all these coordinate systems we use the device coordinate meaning that the location and sizes are specified in terms of pixels.

We will talk more about the device coordinate in detail in some other article. But in the below piece of code, I will start with the client coordinate and then will move to mapping mode and logical coordinate based drawing.

CRect rct;
GetClientRect(&rct);

When you run the SDI application it looks like below (Resized):




The screen marked in blue is termed as client area of the window. The GetClientRect function will help in getting the rectangular marked as blue in the above depiction. We get four values left, top, right and bottom and this is also shown above. Look at the below video that debugs and examines value after calling GetClientRect() function.

3. Mapping Modes


The mapping modes are useful to perform the real world drawing. Say for example; let us say the software utility you are making is useful to perform an engineering drawing. When the drawing is printed out, you want to see the 10 mm line drawn in your monitor, measured exactly as 10 mm in the hard copy, taken from the printer. In this case, we need a way to represent how a single drawing unit measures to the real world measuring standards. MFC supports eight mapping modes which are shown in the below-specified picture:



For MM_TEXT mapping mode, the positive X moves from the default top left corner origin, to the right side. MM_ISOTROPIC and MM_ANISOTROPIC have a user-defined coordinate system. In MM_ISOTROPIC both x, y units are measured equally. But in the MM_ANISOTROPIC mode X and Y can have different units of measure. The above picture also shows for a given "mapping mode" how a single logical unit is measured. The MM_TEXT mapping mode is the default mapping mode used by MFC. Using the SetMapMode function of the underlying Device Context, you can pick any one of the mapping modes shown above.

4. Drawing a Line using mapping Modes


In the OnPaint handler, let us draw a line using MM_TEXT mode. Remember from the previous picture that each unit you specify to the drawing function represents a pixel. For an example, if the length of line 100 means, it is 100 pixels long as the mapping mode is MM_TEXT. Next thing we should remember is that the origin is in the upper-left corner of the window and positive Y goes down. So to see something when you draw, you should specify both x, y units in positive (Look at the picture for reference). OK, let us start drawing the line. Have a look at the below code:

//Sample 02: Draw Lines and see where the Origin is.
dc.MoveTo(0,0);
dc.LineTo(100,100);

In the above code, first, we moved the drawing pen to the Origin 0,0. Then asked to draw the line by specifying the end point as 100,100. When you run the Example you will see a line from origin 0,0 to the point at 100,100. This can be illustrated something like the below one:


For More explanation look at the video: 



What happens if I change the mapping mode from the default MM_TEXT to MM_LOMETRIC? The first thing one should be aware is that the Unit of measure changes from pixels to a millimeter. The second thing is that the Positive Y axis shifts its direction. Below is the Example that uses the MM_LOMETRIC as the mapping mode.

//Sample 03: Set New Mapping Mode
dc.SetMapMode(MM_LOMETRIC);
dc.MoveTo(0,0);
dc.LineTo(100,-100);
For More explanation look at the video: 


5.  Shifting the Origin using SetViewPortOrg


In the previous examples, we saw that how can we set the mapping modes on the device context. Also in the previous examples, the drawing origin is kept in the top left corner of the window, which is default origin. Device context has the capability of changing the origin from the default left corner using the SetViewPortOrg function call.
This function expects the new location in terms of pixel and shifts the drawing origin from top left corner to the new location. Have a look at the below code:

//Sample 01: Client Rectangle.
CRect rct;
GetClientRect(&rct);
//Sample 04.1: Shift the Origin to Screen Center
dc.SetViewportOrg(rct.right/2, rct.bottom/2);

In the OnPaint() handler, first the Origin is shifted from the top left corner to the center of the window. The rct is the CRect object, which has the Client Area dimension in device Co-Ordinate. So we specify the center of the client area in terms of a pixel to the SetWindowOrigin. Below is the picture which shows the shifted Origin:


The code in effect is shown in the above picture. The Red one is the origin before the shift and the blue one is the origin after the shift. Remember, again that we specified the new origin in terms of pixels. In the below code a blue pen is created and then a line is drawn. As there is no mapping mode set yet, the device context takes MM_TEXT as the mapping mode.

//Sample 04.2: Draw a blue Line MM_TEXT
CPen pen(PS_SOLID, 2, RGB(0,0,255));
CPen* OldPen = dc.SelectObject(&pen);
dc.MoveTo(0,0);
dc.LineTo(100,100);

After drawing the blue line, the red line is drawn in the MM_LOENGLISH mapping mode. At this stage, since you have gained a good idea of mapping modes, I am not going to explain that once again.

//Sample 04.2: Draw a Red Line using MM_LOENGLISH
dc.SetMapMode(MM_LOMETRIC);
CPen pen1(PS_SOLID, 10, RGB(255,0,0));
dc.SelectObject(&pen1);
dc.MoveTo(0,0);
dc.LineTo(100,100);
dc.SelectObject(OldPen);
Now running the entire code makes your drawing look like the one shown below:


For More explanation look at the video: 


6. Using the SetWindowOrg


At this stage, you know what the SetViewportOrg does. In a short summary again, the SetViewPortOrg sets the drawing origin by specifying the values in terms of pixels. The SetWindowOrg function set the given logical point as Lower Left Corner of the client Area Window.

Note the difference, View Port Origin is specified in terms of pixels and Window Origin is specified in terms of Logical units, which depends on the currently set mapping mode of the device context. Have a look at the below Example:

//Sample 05.1 Set the Mapping Mode and Viewport origin
dc.SetMapMode(MM_LOMETRIC);
dc.SetViewportOrg(rct.left, rct.bottom);

//Sample 05.2 Set this new location as top left of the client area
dc.SetWindowOrg(-100 , -100);

//Sample 05.3 Draw the Verticle and Horizontal line from the Origin (Viewport)
CPen pen1(PS_SOLID, 1, RGB(255,0,0));
CPen* OldPen = dc.SelectObject(&pen1);
dc.MoveTo(0,0);
dc.LineTo(0,500);
dc.MoveTo(0,0);
dc.LineTo(500,0);
dc.SelectObject(OldPen);

In this example first we set the mapping mode, as MM_LOMETRIC and I do not want to tell what it is. Then we shifted the drawing origin to lower left corner of the screen by specifying a location to be shifted as pixels. Then using the SetWindowOrg function, we specify the logical unit -100, -100 should be at the lower left corner of the window. Once this is done, we draw two lines using Red color Solid Pen. In those lines, one moves from the drawing origin(View Port Origin) to 500 unit in the positive, X direction and other one moves 500 units in positive, Y direction. Have a look at the below picture to understand this as it confuses most of MFC professionals:



For More explanation look at the video: 


Source Code : Download