Click here to Skip to main content
13,740,951 members
Click here to Skip to main content
Add your own
alternative version

Stats

209.3K views
6.9K downloads
123 bookmarked
Posted 29 Jan 2000
Licenced

Creating Alternate GUI using Vector Art

, 6 Feb 2000
Rate this:
Please Sign up or sign in to vote.
Create visually complex, yet programmatically simple, non-rectangular GUIs

Sample Image - flowerpower.gif

Introduction

As Windows Programmers, we know the following to be true:

  • All Windows must be rectangle.
  • All GUI components must have a 3D-chiseled-chrome-look.
  • GUIs look old and tired if they don't follow the look of the most current MS-Office suite or most current version of IE.
  • If you deviate from the above, it will require a large amount of work, add a large amount of bugs, will little or no functional benefit.

Luckily, these "facts" aren't true. I personally believe that we are starting to see a transition from hard-coded, MS-looking GUIs to more unique and sometimes photo-realistic GUIs for applications. Currently, you can see this trend in applications that are "skinned" and commercial applications like the photo-realistic Gizmos. When Apples Os X is release in about a year, I believe we will see an explosion of alternate GUIs for both the Apple and Microsoft platforms.

What Technologies Can Be Used for Alternate GUIs

I feel that part of my job, as a software engineer, is to prepare for this future. To prepare, I have been looking at several technologies:

  • GDI Graphics - This means hard-coding your components with MoveTo, LineTo and other familiar GDI calls. Most of the articles in www.codeproject.com that relate to custom GUI components use this technique. This technique is very flexible, however changes directly affect compiled code.
  • Bitmap Graphics - This technique uses bitmaps and hit regions to create a GUI component. This is a common technique - several articles can be found on www.codeproject.com that discuss this technique. This technique can be used with "skinning" to apply the actual graphical elements of the GUI at runtime. WinAmp is a good example of using this technique. This technique is straightforward (though I wouldn't say it's trivial). However, bitmaps don't scale well, so if your GUI needs to resize, you usually have to define bitmaps for each of the sizes you want to support.
  • Vector Graphics - Vector graphic components are just as easy for a graphic artist to create as bitmap graphics. With the right tools, it is trivial to convert vector graphics into a form that is directly usable in your application. Vector graphics have several advantages. They are easy to paint, they are easy to turn into regions, they are easy to hit-test, and they translate, rotate and scale easily. This article shows how to use this technique to create an interesting GUI. This approach should work with "skinning". Hopefully, you will see how trivial vector graphics are to used in applications.
  • Rendered Graphics - This technique allows GUI components to be rendered on the fly. This means there is a definition for the components of the image that are rendered using an rendering engine such as DirectX or OpenGl. This technique is applied currently to games. However, there is no reason that photo-realistic GUI components couldn't use this same approach. This approach should work with "skinning".

Creating an GUI using Vector Art

How to Get Vector Art

I'm employed at a company where I have the good fortune to work closely with Industrial Artists and Human Factors Engineers. For the last several years, part of my job has been converting the Industrial Artist's art and Human Factors Engineer's behavior into elements for Windows applications. This has reinforced my opinion that most programmers are lousy artists and even worse interface-designers (just so you know, I include myself in this broad, overly simplified generalization).

At my company, the preferred tool for use by an Industrial Artist is Adobe Illustrator. This tool allows the creation of art as bitmaps, vectors, or a combination of both. It is my observation is that artists prefer working in vector art. This is for practical reasons. Vectors can be defined as individual objects that can be ordered, translated, rotated, and scaled independently.

I've also observed that Windows programmers have a mental block. All graphic problems are solved with bitmaps. Personally, I hate bitmaps. They don't easily scale or rotate. They tend to be large. And you have to associate touch regions with the bitmap if you want to define any interesting behavior to them. In other words, bitmaps are difficult to work with and the result is often poor.

Since the artists I associate with prefer working with vector art anyway, I've been asking for the vector form of their art for my projects. I then run the files through a tool that I've written that extracts the vectors and text and converts them into C++ code. This article shows how to use this type of data to create interesting applications.

Disclaimer: Do not assume that the project shown here is either an example of good art or of good Human Factors design. It's just an example of what you can do with vectors.

Thinking in Vectors

Vector art is drawn the same way a painter paints. Elements of the image are painted from back to front with the later elements possibly obscuring some of the earlier elements. This means that you should never change the order that items are drawn. It also means that you should hit-test in the reverse order that items are drawn.

An Example Polygon Conversion

The following is an example of the output of a red circle. I created the drawing with CorelDraw. Saved the output as a Windows Meta File. I then run that file though a tool that I wrote that extracts, polygons, polylines, and text. The following code is output from this tool.

#include "StdAfx.h"
#include "ArtCode.h" // Contains code that does hit-test, drawing, and region 
creation.

static RECT bounds = {20, 20, 15980, 15740};

static POINT Polygon0Points[] = {
	{8000, 15740}, {8409, 15730}, {8814, 15700}, {9212, 15650}, {9605, 15580}, 
{9990, 15492}, {10368, 15386}, {10739, 15262}, {11101, 15121}, {11454, 14963}, 
	{11798, 14789}, {12133, 14600}, {12456, 14395}, {12769, 14176}, {13071, 
13942}, {13361, 13694}, {13638, 13434}, {13903, 13161}, {14154, 12875}, 
{14391, 12578}, 
	{14614, 12270}, {14822, 11951}, {15015, 11622}, {15191, 11283}, {15351, 
10935}, {15495, 10578}, {15620, 10213}, {15728, 9841}, {15818, 9461}, 
{15888, 9074}, 
	{15939, 8682}, {15970, 8284}, {15980, 7880}, {15970, 7477}, {15939, 7078}, 
{15888, 6686}, {15818, 6299}, {15728, 5920}, {15620, 5547}, {15495, 5182}, 
	{15351, 4825}, {15191, 4478}, {15015, 4139}, {14822, 3810}, {14614, 3490}, 
{14391, 3182}, {14154, 2885}, {13903, 2600}, {13638, 2326}, {13361, 2066}, 
	{13071, 1819}, {12769, 1585}, {12456, 1365}, {12133, 1160}, {11798, 971}, 
{11454, 797}, {11101, 639}, {10739, 498}, {10368, 374}, {9990, 268}, 
	{9605, 180}, {9212, 111}, {8814,  60}, {8409,  30}, {8000,  20}, {7590,  
30}, {7186,  60}, {6787, 111}, {6395, 180}, {6009, 268}, 
	{5631, 374}, {5261, 498}, {4899, 639}, {4545, 797}, {4201, 971}, {3867, 
1160}, {3543, 1365}, {3230, 1585}, {2929, 1819}, {2639, 2066}, 
	{2361, 2326}, {2097, 2600}, {1846, 2885}, {1608, 3182}, {1386, 3490}, 
{1178, 3810}, {985, 4139}, {808, 4478}, {648, 4825}, {505, 5182}, 
	{379, 5547}, {272, 5920}, {182, 6299}, {112, 6686}, { 61, 7078}, { 30, 
7477}, { 20, 7880}, { 30, 8284}, { 61, 8682}, {112, 9074}, 
	{182, 9461}, {272, 9841}, {379, 10213}, {505, 10578}, {648, 10935}, {808, 
11283}, {985, 11622}, {1178, 11951}, {1386, 12270}, {1608, 12578}, 
	{1846, 12875}, {2097, 13161}, {2361, 13434}, {2639, 13694}, {2929, 13942}, 
{3230, 14176}, {3543, 14395}, {3867, 14600}, {4201, 14789}, {4545, 14963}, 
	{4899, 15121}, {5261, 15262}, {5631, 15386}, {6009, 15492}, {6395, 15580}, 
{6787, 15650}, {7186, 15700}, {7590, 15730}, {8000, 15740}, 
};

static PolygonEntry Polygon0 = {
	"Polygon0",
	{20, 20, 15980, 15740},
	RGB(218, 37, 29),
	1, // PolyFillMode
	129,
	Polygon0Points,
};

// Draw Vector image - will scale to clientRect so if you want
// to maintain the images aspect ratio please make sure the clientRect
// passed also maintains that aspect ratio of the object.
void DrawObject(CDC* pDC, const CRect& clientRect) {
	Draw(pDC, Polygon0, clientRect, bounds);
}

// Creates a region. Regions can be used for several things.
// However the result of this call is usually used to create a
// Window Region.
void CreateRegion(CRgn& rgn, const CRect& clientRect) {
	CreateRegion(rgn, Polygon0, clientRect, bounds);
}

// TRUE on an element in the vector drawing is hit.
BOOL HitTestObject(const CPoint& point, const CRect& clientRect) {
	if (HitTest(Polygon1, clientRect, bounds, point)) return TRUE;
	return FALSE;
}

// Returns the aspect ratio of the image. To use this you do the following:
// newWidth = AspectRatio()*window.Height();
double AspectRatio() {
	CRect objectRect(bounds);
	return ((double) objectRect.Width())/((double) objectRect.Height());
}

Creating a Dialog Application

To modify your dialog app, all you need to do is set the window region to the regions of the combined vectors and then handle OnPaint messages. The logical place to set the window region is in OnInitDialog.

OnInitDialog

This code does the following:

  • Sets the windows rect to the aspect ratio of the vector drawing
  • Gets the region for the combined vectors in the drawing
  • Sets the window region to the region of the drawing
BOOL CFlowerPowerDlg::OnInitDialog()
{
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	m_downItem = None;
	
	// Maintain aspect ratio of drawing
	CRect rect;
	GetWindowRect(&rect);
	rect.right = ((int) (rect.Height()*AspectRatio()))+rect.left;
	CRgn wndRgn;

	// Size Window to new aspect ratio
	MoveWindow(&rect, FALSE);

	// Get Screen Rect and convert to client coords
	GetWindowRect(&rect);
	ScreenToClient(&rect);
	
	// Create a region for our window in client coords
	CreateRegion(wndRgn, rect);

	// Set Window region in client coords - the MS docs say this call is in 
	// Screen Coords, but only client coords work.
	SetWindowRgn(wndRgn, TRUE);
	
	CDialog::OnInitDialog();
	return TRUE;  // return TRUE  unless you set the focus to a control
}

OnPaint

OnPaint is the stock output from the AppWizard expected for the non-iconic drawing. When drawing non-icon version of the dialog, you do the following:

  • Get the Windows dc
  • Create a memdc (to eliminate flickering on redraws)
  • Get the Window Rect (we are taking over the entire dialog area not just the client)
  • Cover it to Client Coords
  • Draw the Vector Image
void CFlowerPowerDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		if (!m_bDialogUp) {
			CWindowDC dc(this);
			CMemDC pDC(&dc);
			CRect rect;
			GetWindowRect(&rect);
			ScreenToClient(&rect);
			DrawObject(&pDC, rect);
			CDialog::OnPaint();
		}
	}
}

Creating Your Own Vector Code

To create your own C++ code out of vector art, I've included the application I've written called ArtCode.exe. Just so you know, this application is a complete hack. It was designed for one purpose, to turn vector art to code with the least work possible with the tools that I already had. I had Adobe Illustrator and CorelDraw. Because of that, the process of getting your vector art to C++ requires a reasonably current copy of CoreDraw (I've successfully used versions 6-9 for this). Here's the process (and yes, it is quirky -- but it works):

  • Import your vector art into CorelDraw.
  • Select the components you wish to convert to code.
  • Export the selected graphic component in Adobe Illustrator format (this step reduces the number of graphic primitives to the ones supported in the ArtCode.exe program).
  • Import that Adobe Illustrator File.
  • Export the art as a Windows Metafile (I suggest you convert text to curve).
  • Open the Windows Metafile with ArtCode.exe.
  • Select the Edit|Copy command (this generates the code and places it on the clipboard).
  • Paste the code into VC++.
  • Add the ArtCode.h and ArtCode.cpp files from the FlowerPower project to your project.

Additional Resources

Rotation, translation, and Scaling of vector art is easy, but if you don't recall how to do this from your linear algebra class (mine was 20+ years ago), you will need either a good computer graphics book or a good linear algebra book. All you do is set up a transformation matrix and do a matrix multiply to perform one or more of these operations. Once you see it, you'll discover how simple this really is. A decent calculator helps too (I love my TI-92).

Comments about the Tool

The ArtCode.exe program is completely unsupported. Please do not send me email asking for changes or to help you troubleshoot some problem you are having with it. It is very, very unlikely that I will respond to any such email. If you do get stuck and absolutely need help, you might try one of the discussion forums on www.codeproject.com.

The tool will not work if you try to open your run-of-the-mill Windows Metafiles. The tool supports a very, very small subset of Windows Metafile commands so you are guaranteed to get no graphics or a subset of your graphics if you import a typical WMF. The step of exporting to Adobe Illustrator format first is absolutely required in most cases.

Conclusion

The point of this article isn't to cause any serious changes in how you design GUIs. The goal is to stimulate some thought about where GUI development is going, and to help you prepare for the future that you see coming. It also should show you how much easier working with vector graphics can be than working with bitmaps graphics.

Caveats

You may use the code you find included in this project anyway you like. However, the art was derived from my legal copy of CorelDraw. I believe that it is legal for me to use the derived version of the art in my code, however please don't use this art in your project unless you own CorelDraw.

License

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.

Share

About the Author

Keith Rule
Software Developer (Senior)
United States United States
I work at Tektronix in Beaverton OR. I've been programming for fun since 1975 (I started while in a Computer Explorer Scout group in Spokane WA). I've been programming in C since 1979 and I've been working professionally since 1983.

I really enjoy www.codeproject.com. It has saved me an incredible amount of time. I only hope my small contributions have given back some of what I've taken.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralThanks Pin
Hooman_Kh7-Jul-14 14:35
memberHooman_Kh7-Jul-14 14:35 
Generaladvanced vectors Pin
a_matseevsky11-Jun-08 1:58
membera_matseevsky11-Jun-08 1:58 
GeneralStrange problem adding some parts of this program on mine Pin
ROCKISDEAD1-Dec-05 4:05
memberROCKISDEAD1-Dec-05 4:05 
GeneralResolution Pin
loneill71629-Apr-04 12:01
memberloneill71629-Apr-04 12:01 
GeneralGraphics programs Pin
speedpacer30-Sep-03 14:29
memberspeedpacer30-Sep-03 14:29 
Generala new tool for converting wmf Pin
__^__29-Aug-02 18:02
suss__^__29-Aug-02 18:02 
GeneralRe: a new tool for converting wmf Pin
Chris Losinger29-Aug-02 18:06
memberChris Losinger29-Aug-02 18:06 
GeneralRe: a new tool for converting wmf Pin
Keith Rule10-Jan-03 11:50
memberKeith Rule10-Jan-03 11:50 
QuestionCan any body explain abt Meta Files? Pin
4-Aug-01 1:37
member4-Aug-01 1:37 
QuestionCan any body explain abt Meta Files? Pin
4-Aug-01 1:37
member4-Aug-01 1:37 
GeneralSomeone please explain it to me. Pin
JongAm Park26-Jan-01 14:52
memberJongAm Park26-Jan-01 14:52 
GeneralDiscussion Pin
Buck3-Feb-00 10:01
memberBuck3-Feb-00 10:01 
GeneralRe: Discussion Pin
David Gallagher30-Oct-00 8:58
memberDavid Gallagher30-Oct-00 8:58 
Colin,
I'm a controls engineer , and am finding thet the windows interface is not the ideal for factory floor touch screen control. I am currently working on a project where I would like to ( if time would ever permit ) define my own UI , especially menus. Just thought I would let you know of uses for re-defining the UI.
David Gallaghe
GeneralRe: Discussion Pin
Buck30-Oct-00 9:30
memberBuck30-Oct-00 9:30 
GeneralRe: Discussion Pin
Keith Rule30-Oct-00 12:50
memberKeith Rule30-Oct-00 12:50 
Generaltool to extract vectors Pin
Johan De Mulder31-Jan-00 1:45
memberJohan De Mulder31-Jan-00 1:45 
GeneralRe: tool to extract vectors Pin
Chris31-Jan-00 13:08
memberChris31-Jan-00 13:08 
GeneralRe: tool to extract vectors Pin
Keith Rule31-Jan-00 14:56
memberKeith Rule31-Jan-00 14:56 
GeneralRe: tool to extract vectors Pin
Alex2-Feb-00 1:26
memberAlex2-Feb-00 1:26 
GeneralRe: tool to extract vectors Pin
Keith Rule2-Feb-00 15:05
memberKeith Rule2-Feb-00 15:05 
GeneralRe: tool to extract vectors Pin
kob-kob22-Apr-09 5:13
memberkob-kob22-Apr-09 5:13 
GeneralRe: tool to extract vectors Pin
Keith Rule23-Apr-09 10:02
memberKeith Rule23-Apr-09 10:02 
GeneralCaveat Pin
Scotty Dawg30-Jan-00 23:34
sussScotty Dawg30-Jan-00 23:34 
GeneralRe: Caveat Pin
Chris31-Jan-00 13:08
memberChris31-Jan-00 13:08 
GeneralRe: Caveat Pin
Keith Rule31-Jan-00 14:52
memberKeith Rule31-Jan-00 14:52 

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 | Cookies | Terms of Use | Mobile
Web05-2016 | 2.8.180920.1 | Last Updated 7 Feb 2000
Article Copyright 2000 by Keith Rule
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid