Click here to Skip to main content
12,816,320 members (30,566 online)
Click here to Skip to main content
Add your own
alternative version


31 bookmarked
Posted 24 Oct 2001

A General Polygon Management Routine

, 24 Oct 2001
Rate this:
Please Sign up or sign in to vote.
A class to make handling polygons easier.


I found myself doing a lot of drawings recently of simple polygons, such as outline shapes. The problem was that these had to be displayed in a variety of rotations. One such shape was a nice compass arrow, which I use in my accompanying essay on doing self-registering custom controls. The complete control is reproduced here. What this essay concentrates on is how I made a nice rotating arrow.

The idea here is to create a compass that points to where an object is heading, using geographic coordinates. Much of the detail of the drawing is discussed in the accompanying essay. 

To create these simple monochrome shapes, I spent a couple hours writing a trivial editor which allows me to enter a set of x,y coordinates. No mousing, nothing terribly sophisticated, but all I needed was a list of x,y coordinates, not a metafile or anything else elaborate. The most complex drawing I have has about 20 endpoints. I used my little vector editor program, which is described in an accompanying essay.

Here is the set of points that defines the compass needle. The nominal coordinate system is a 100-unit coordinate system ranging ±50.

-14, 30
-5, 38
0, 46
5, 38
14, 30
4, 34
4, -36
0, -40
-14, -46
-4, 34


class CPolygon
       CPolygon() { heading = 0.0; scaling = 1.0; }
       BOOL Read(const CString & filename);
       BOOL Load(UINT resid);
       BOOL Load(LPCTSTR resource);
       CRect GetInputBB();
       CRgn * GetRgn();
       CRect Transform(double angle, double scale);
       void Draw(CDC & dc, CDoublePoint pt);
       bool IsDefined() { return points.GetSize() > 0; }
       CArray<<a href="">CDoublePoint</a>



Constructor. Initializes the polygon. The polygon contains no points.


BOOL CPolygon::Read(const CString & filename)
const CString & filenameThe name of a file to read.

Reads a set of points from the specified file. If there is an error, returns FALSE. If there is an error, the contents of the points array is not defined. Minimal syntax checking is done on the input, since it is assumed that the input comes from a program.


BOOL CPolygon::Load(UINT resid)
BOOL CPolygon::Load(LPCTSTR resourcename)
UINT residThe resource ID of a POINTS resource
LPCTSTR resourcenameThe name of a POINTS resource

Loads a set of points from a resource segment. The resource segment type must be POINTS. The two forms determine if the name is a numeric resource ID or a string name.

To include a set of points in a resource segment, add the following style of declaration to the \res\projectname.rc2 file:

resource_name POINTS DISCARDABLE "\\res\\filename"

For example, to add the arrow resource, I did


Because I did not define a symbol such as 

#define IDP_ARROW 1101

I use the LPCTSTR version of the method.

Copy the file (in my case, arrow.pln) to the \\res subdirectory of your project.


CRect CPolygon::GetInputBB( )

This returns a CRect which is the bounding box of the polygon as it was read in, before any rotational transformation is done to it. The bounding box is the smallest rectangle that completely encloses all the points of an object.


CRgn * CPolygon::GetRgn( )

Returns the region of the transformed (rotated and scaled) polygon. This can be used to invalidate the area occupied by the object.


CRect Transform(double angle, double scale, BOOL force = FALSE)
double angleThe angle of rotation, expressed in geographic coordinates
double scaleThe scaling factor. This is interpreted as relative to the points read in for the definition.
BOOL forceForces a recomputation, even if the internal optimizations determine that the existing set of transformed points would be valid.

This transforms the polygon points according to the specified angle (expressed in geographic coordinates, with 0.0 North, 90.0 East, 180.0 South and 270.0 West) and scaling. The result is an internal structure which contains the transformed points, suitable for theDraw method. The CRect returned is the bounding rectangle of the transformed points, or the rectangle (0,0,0,0). As an optimization, if the angle and scale are the same as the previous Transform call, nothing is computed and (0,0,0,0) is returned. If you need to force the recomputation to obtain the bounding box return, use the third parameter, force, and set it TRUE which bypasses the optimization.

Note that the CRect is in mapped coordinates, with y values increasing upwards and x values increasing rightwards; the assumption is that the points are centered around a 0,0 axis which is at the logical center of the object. Therefore, IsRectEmpty will always return TRUE. If you need to check for an empty rectangle, you may need to first make a copy and call the NormalizeRect method on that copy (if you don't need to preserve the CRect, you can call NormalizeRect on the result).

This method must be called before the Draw method is executed. Draw will always draw the polygon based on the current transformation established by Transform. If Transform has not been called since the data was loaded, the transformed points array is empty.

The transformation is a classic rotation around the center point 0,0, according to the transformation matrix shown below. Given two points x,y, a scaling in x sx, and a scaling in y sy, the new points x', y' are computed according to the following matrix multiplication 

        _                   _
       |  cos Ø    sin Ø  0  |
[x y 0]|  -sin Ø   cos Ø  0  |=[sx*((cos Ø)*x-(sin Ø)*y)
       |_    0     0      0 _|  sy*((sin Ø)*x+(cos Ø)*y) 0]

(The third column is added to make the matrix multiplication work out, and then discarded).

In my case, I only allow isotropic (sx == sy) scaling.


BOOL CPolygon::IsDefined( )

Returns TRUE if the polygon is defined by a set of points. Returns FALSE if the array of points is empty. Note that if there is an error in reading, the array of points is usually empty.


void CPolygon::Draw(CDC & dc, CDoublePoint pt)
CDC & dcThe DC used to draw the object. 
<a href="">CDoublePoint</a> ptThe point in the DC, expressed in logical coordinates, at which the image is drawn.

Draws the transformed polygon in the specified DC, at the location specified by the point. The DC is assumed to be in a mapped mode with y increasing upwards (rather than downwards as in MM_TEXT mode). The DC is unmodified upon return. The Transform method must be called before calling Draw to create the set of transformed points. Failing to call Transform will mean that the last transformation will be used; if no transformation has been performed, the transformation set is empty and no drawing will appear.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Joseph M. Newcomer
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

You may also be interested in...

Comments and Discussions

GeneralGreat! A few enhancements and fixes to the compass... Pin
brownfox23-Apr-07 23:42
memberbrownfox23-Apr-07 23:42 
Thanks a lot!
I have reused your code in my homebrew navigation program.
A few ehancements were made to the compass. A complete code is not worth typing, I just point to areas to pay attention to.
1. compass.cpp, line 345 (OnPaint, near the bottom of the function)
arrow.Transform(...) - the numerator and denominator should be swapped.
2. compass.cpp, from line 291 (OnPaint, drawing cardinal letters)
All this stuff with if(theta)...else if... may be easily replaced with
CSize textSize = dc.GetTextExtent(text);
dc.SetTextAlign(TA_BOTTOM | TA_CENTER);
y -= / 2;
This will not greatly affect the published version, but if you ever want a rotating dial (like I do), this will help.
3. compass.cpp, from line 343 (OnPaint, drawing an arrow)
Note that using arrow.GetInputBB() results in arrow always centered and scaled to fit the whole diameter. This may not be what you expect if the arrow is not centered - for example, like a pointer of a typical automotive gauge. I have made a new method CPolygon::GetCenteredBB() using some combinations of max() and min() with and .bottom.
4. I find useful to add CPolygon::AddPt(double x, double y) {guess what?} to build a polygon at runtime.

QuestionHow implement in VB.Net Pin
dox228-Mar-07 6:56
memberdox228-Mar-07 6:56 
Question+ 0.5 ? Pin
Mauricio Ritter21-Jan-02 2:47
memberMauricio Ritter21-Jan-02 2:47 
AnswerRe: + 0.5 ? Pin
Anonymous15-Mar-02 2:18
memberAnonymous15-Mar-02 2:18 
GeneralThis is great - thanks Joseph! <eom> Pin
J.G. Hattingh27-Oct-01 0:16
memberJ.G. Hattingh27-Oct-01 0:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170308.1 | Last Updated 25 Oct 2001
Article Copyright 2001 by Joseph M. Newcomer
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid