Here is a Speedometer class,
CSpeedoMeter v1.0. I had been looking for a speedometer control out there for a while but I couldn’t find one. Then I decided to give it a try. I thought it would be pretty simple, using simple trigonometric calculation to do rotation. If you are wondering why you need a rotation calculation, imagine you are in front of your car's dashboard. When you press the pedal you will see something rotating and suddenly your car starts accelerating. Exactly, you need to rotate a needle!
Basically, my approach is to have the background of the speedometer as a bitmap. Unfortunately, to make the needle as a bitmap is a bit messy. There is not an easy way to rotate bitmap in GDI API. But, we can just draw the needle as a line. You only need to take care of two points, while you connect with a straight line. In fact, you are actually controlling only one point. That point will be rotated and a line segment will be drawn representing the needle at any particular speed. Simple enough!
The Simple Mathematics
To make it happen, you need to calibrate your Speedo's needle. I am assuming the 0 degree start from 6 o’clock. As with most of the real Speedo controls in a real car, the needle is initially set at about 7:00 o’clock direction (30 degree clock-wise from 6 o’clock. Our example however uses initial offset at 40 degree). Remember, one full circle would represent 360 degrees. If let’s say, we assume one degree will represent 1 km/h, then life is easy. For each 1 km/h delta, it will represent a rotation of 1 degree clock-wise.
Therefore, once we can calculate the rotation and are able to draw the needle at any particular angle, then that’s about it, a speedometer is not far away from your hand. We just need to make some assumptions. So, for example let’s say, updating the needle to 240 km/h, you need to rotate the needle by 240 degrees + offset. Remember we use offset = 40, which makes the rotation required to set the needle to 240 km/h, to 280 degrees. The basic formula is:
RotationAngle = OffsetAngle + NewSpeed;
I am sure it is simple enough to be understood, so I would just teach you how to use my control. Another thing worth mentioning is, don’t forget about drawing the appropriate background image that is consistent with your assumption. In my case, I created a bitmap with the 0 speed set at 40 degrees from 6 o’clock clock-wise. I know my bitmap is not the coolest in this planet, but to make a start I think it is not too bad, yeah!
Using the code
There are a few files you need to copy to your project directory. They are:-
- matrix2x2.h & matrix2x2.cpp
- vector2.h & vector2.cpp
- speedometer.h & speedometer.cpp
The matrix2x2 and vector2 are also written by me for doing the rotation stuff. They become so handy from time to time when I develop various projects. The speedometer class is obviously the core engine of the Speedo.
After you put them in your project directory, create a dialog MFC project and add the above files to the project. Then, just build the project. I am sure it will fail. Why? Because you haven’t linked your project with winmm.lib. Please do so by going to Project-><ProjectName> Properties…
Go to linker and add winmm.lib on Additional Dependencies field. Just wanna inform you, I use Visual Studio .NET 2003 to build the project. For VS 6, please refer to VS documentation on how to link libraries to your project.
Next, you need to create a static control on your dialog. Size it to 400x400 to fit my bitmap background. Add a control variable to the static control, say
m_speedometer. Then under your XXXDlg.h, change the type of
m_speedmeter control from
CSpeedoMeter. Don’t forget to add
#include speedometer.h at the top of your XXXDlg.h file to make sure it understands what
Wait a minute, you are still 2-3 steps away from using the control. The next step would be to initialize the speedometer. To do this, I prefer to create an initialization function in your
XXXDlg (in my demo example, it is
CspeedoMeterDlg) class, say
void InitializeSpeedoMeter(). Add a few lines of code to the function body to set the bitmap, needle thickness, and its color. You should call the initialization routine inside the
I am assuming that you know how to import a bitmap to your project. Otherwise, simply go to Project->Add Resource on the main menu, then import bitmap. Don’t forget to name the bitmap (e.g.,
IDB_SPEEDOMETER). Last but not the least, call
InitSpeedoMeter in the
OnInitDialog() handler, just under
// TODO: Add extra initialization here.
OK, now you are ready to use the control. One easy way to try the control is to add
OnMouseWheel(…) handler. Then based on the value of
zDelta, you update the speedometer speed through
UpdateSpeed(float speed) interface or
Accelerationg(float delta) interface. My demo, uses
OnTimer(…) handler to do a simple but interesting simulation.
Points of Interest
Doing graphics programming is always exciting because no matter what, you will play with math, and if you are lucky, you will play with physics. It is so cool!. Any, constructive input, comments and expression of interest can be sent to my email address.
- V1.0 - Working speedometer.