Abstract
This article presents a detailed discussion of using multiple graphic
layers to create complex dynamic Windows Forms controls.
Often programmers are faced with significant challenges when
implementing a complex graphic object. When multiple graphics layers
are incorporated, the complexity can be significantly reduced. An
additional benefit, significant execution time reduction, may also be
obtained.
Introduction
Recently, while developing a demonstration of an interthread messaging
system, I needed to create a simulation of a number of aircraft
cockpit instruments. I really didn't need to create the instrument
graphics, but it was a challenge that I couldn't resist. One of these
instruments is known as an attitude indicator.
This instrument
displays both pitch (nose up and down) and roll (wing up and down). In
modern aircraft, this instrument has been replaced to a large extent
by the electronic Attitude and Heading Reference System (AHRS).
However, the attitude indicator can be driven by only two inputs
(pitch and roll). Therefore, by using the attitude indicator, I can
simulate the aircraft's attitude without resorting to a simulation of
the AHRS.
The attitude indicator is a composite control. A small symbolic
aircraft is fixed within the instrument. The top half of the
instrument dial is blue, representing the sky; and the bottom half is
brown, representing the ground. A bank index at the top of the
instrument shows the angle of bank marked on the banking scale with
lines and triangles that represent 0°, 10°, 20°, 30°, 45°, and 60°. A
pitch index at the center of the instrument shows the amount of pitch.
The symbolic aircraft moves up and down over the pitch index while the
roll is represented by a clockwise or counterclockwise rotation of the
sky-ground bank index.
In the following discussions, properties that are specified by the
developer are displayed in
BoldMixedCase text.
Variables, used internally by the software, are displayed in
italicized_lowercase text with
underscores separating words.
Table of Contents
The symbol
returns the reader to the top of the Table of Contents.
Visual Properties
The AttitudeIndicator control has properties that affect the user's
visual image. The developer specifies the control's top-left corner
by dragging the control from the ToolBox to a position on the form.
This position becomes ( 0, 0 ) in the control's
graphic environment.
The height of the AttitudeIndicator control is the same as the width
of the control. The width of the control is specified using the
resizing handles of the
Visual Studio Designer
[^]
or by setting a new value for the control's
Size
[^]
property or by specifying a value for the
ControlWidth
property. The control's width is limited to multiples of 50 from 150
to 500. If the control
is resized, the values of all of the control's components are
recomputed based upon the control's new width.
The CurrentPitch and
CurrentRoll properties are
the means by which pitch and roll, respectively, are communicated to
the control.
A change in either value will cause the display to be updated. Both
CurrentPitch and
CurrentRoll properties accept
decimal values.
Implementation
With the exception of the background and the symbolic aircraft, the
control's graphic components are dynamic and reflect the aircraft's
instantaneous pitch and roll. This makes the attitude indicator a
relatively complex instrument to simulate. However, if the graphic is
broken down into separate graphic layers, the complexity can be
reduced and the responsiveness of the control can be significantly
increased.
For the attitude indicator I chose seven graphic layers:
Background | | The instrument body layer. |
Stationary | | The symbolic aircraft layer. |
SkyGround | | The blue/brown layer. |
Pitch | | The pitch scale layer. |
TopBottom | | The caps that appear at the top and bottom of the SkyGround layer.
|
Roll | | The bank index (roll) scale layer. |
Indicator | | The layer onto which the SkyGround, Pitch, TopBottom, and Roll layers
are combined.
|
With the exception of the Indicator layer, each layer is created once
and is recreated only on a resize event. It is only during the drawing
of the Indicator layer that existing SkyGround, Pitch, TopBottom, and
Roll layers are combined (translated and rotated) to create the
aircraft attitude image. In the OnPaint event handler, the Background,
Indicator, and Stationary layers are combined.
Layering
Readers are reminded that, in the graphics environment, objects that
are drawn later override objects that were drawn earlier. This is
true when layers are created or combined. So the drawing order for
layers is:
Background |
Indicator |
SkyGround | Pitch | TopBottom | Roll |
|
Stationary |
Initialization
The attitude indicator is implemented as a Windows Forms Control. The
dimensions of all of the graphics are determined by the control's
width that can vary from 150 pixels to 500 pixels in width,
incremented by 50 pixels. The OnCreateControl and OnResize event
handlers invoke
revise_geometry_values. That
method recomputes the variables used to draw the control's components.
It also forces all layers to be redrawn.
Background Layer
The Background layer is the bottommost layer displayed in the control.
The width and height of the Background layer equal the width and
height of the control. Drawing the Background layer is composed of the
following steps:
1. Draw and fill a rounded rectangle
2. Draw and fill an instrument circle
3. Draw an inner circle, inside of which all other
components are drawn
Once drawn, unless the AttitudeIndicator control is resized, the
Background layer is never redrawn.
Probably the only step of interest is drawing a rounded rectangle.
GraphicsPath rounded_rectangle_path ( int x,
int y,
int width,
int height,
int radius )
{
int diameter = 2 * radius;
GraphicsPath path = new GraphicsPath ( );
path.StartFigure ( );
path.AddArc ( ( x + width - diameter ),
( y + height - diameter ),
diameter,
diameter,
0.0F,
90.0F );
path.AddArc ( x,
( y + height - diameter ),
diameter,
diameter,
90.0F,
90.0F );
path.AddArc ( x,
y,
diameter,
diameter,
180.0F,
90.0F );
path.AddArc ( ( x + width - diameter ),
y,
diameter,
diameter,
270.0F,
90.0F );
path.CloseFigure ( );
return ( path );
}
GraphicsPath rounded_rectangle_path ( Rectangle rectangle,
int radius )
{
return ( rounded_rectangle_path ( rectangle.X,
rectangle.Y,
rectangle.Width,
rectangle.Height,
radius ) );
}
Stationary Layer
The Stationary layer contains those components of the
AttitudeIndicator that must appear on top of all other
AttitudeIndicator components. These include the symbolic aircraft and
the fixed roll pointer.
The image is created from thirteen points, drawn or filled in various
ways. The order in which the Stationary layer components are drawn is
important.
Once drawn, unless the AttitudeIndicator control is resized, the
Stationary layer is never redrawn.
SkyGround Layer
The SkyGround layer contains the visual depiction of the sky and
ground.
The sky is drawn from the top to the bottom, using a Linear
Gradient Brush. The ground is drawn as two rectangles, both of the
same color.
The two rectangles are separated by a distance equal to the ground
portion of the Pitch layer. This allows the Pitch layer to be moved up
and down as the aircraft pitch varies.
Once drawn, unless the AttitudeIndicator control is resized, the
SkyGround layer is never redrawn.
Pitch Layer
The Pitch layer depicts the pitch index at the center of the
instrument. It shows the amount of aircraft pitch.
The Pitch layer contains a pitch scale and a portion of the ground.
These two components fit into the gap between the ground rectangles of
the SkyGround layer.
Once drawn, unless the AttitudeIndicator control is resized, the Pitch
layer is never redrawn.
TopBottom Layer
The TopBottom layer compliments the SkyGround layer by providing
upper and lower caps to that layer. It is purely cosmetic in nature.
Once drawn, unless the AttitudeIndicator control is resized, the
TopBottom layer is never redrawn.
Roll Layer
The Roll layer depicts the bank index at the top of the instrument. It
shows the angle of bank marked on the banking scale with lines and
triangles that represent 0°, 10°, 20°, 30°, 45°, and 60° of aircraft
roll.
Once drawn, unless the AttitudeIndicator control is resized, the Roll
layer is never redrawn.
Indicator Layer
The Indicator layer provides the canvas for the dynamic effects of
aircraft pitch and roll. Whenever pitch or roll change, the Indicator
layer is redrawn.
In the Indicator layer, each of the SkyGround, Pitch, TopBottom, and
Roll layers are combined (translated and rotated) to create the
aircraft attitude image.
The steps taken to create this image in the Indicator layer are:
- A
clipping
region
is defined. This prevents any subordinate layer from
overflowing the control bounds (set in the OnCreateControl and
OnResize event handlers).
- The SkyGround layer image (bitmap) is rotated to the aircraft's
CurrentRoll and drawn into the
Indicator layer.
- The x- and y-coordinates for the Pitch layer are computed from the
aircraft's
CurrentPitch; the Pitch
layer image (bitmap) is rotated to the aircraft's
CurrentRoll; the origin of the
Indicator layer is translated to the x- and y-coordinates computed
earlier; the rotated Pitch layer image is drawn into the Indicator
layer.
- The TopBottom layer image (bitmap) is rotated to the aircraft's
CurrentRoll and drawn into the
Indicator layer.
- The Roll layer image (bitmap) is rotated to the aircraft's
CurrentRoll and drawn into the
Indicator layer.
The method used to rotate the bitmaps was:
Bitmap rotate_bitmap ( Bitmap bitmap,
float angle )
{
Bitmap return_bitmap;
return_bitmap = new Bitmap ( bitmap.Width,
bitmap.Height );
using ( Graphics graphic = Graphics.FromImage (
return_bitmap ) )
{
graphic.TranslateTransform (
( float ) bitmap.Width / 2.0F,
( float ) bitmap.Height / 2.0F );
graphic.RotateTransform ( angle );
graphic.TranslateTransform (
-( float ) bitmap.Width / 2.0F,
-( float ) bitmap.Height / 2.0F );
graphic.DrawImage ( bitmap, new Point ( 0, 0 ) );
graphic.ResetTransform ( );
}
return ( return_bitmap );
}
Demonstration
The demonstration program depicts how the AttitudeIndicator control
reacts to changing pitch and roll. It also allows choosing which
layers will be displayed.
When the program is launched, the Background, Indicator, and
Stationary layers are displayed. If it is desired that layers that
make up the Indicator layer be displayed, the Indicator layer checkbox
must be cleared. This is required because the Indicator layer is
a composite layer, made up of the SkyGround, Pitch, TopBottom, and
Roll layers.
Conclusion
This article has presented a detailed discussion of using multiple
graphic layers to create complex dynamic Windows Forms controls.
References
Aircraft Cockpit Instruments
For interested readers, I have included the following two links. A
complete set of FAA Aviation Handbooks and Manuals can be found at the
FAA
site
[^].
Programming
Development Environment
The AttitudeIndicator control was developed in the following
environment:
Microsoft Windows 7 Professional Service Pack 1 |
Microsoft Visual Studio 2008 Professional |
Microsoft .Net Framework Version 3.5 SP1 |
Microsoft Visual C# 2008 |
Downloads
In addition to the AttitudeIndicator control (the main topic of this
article), the downloads also include a number of additional aircraft
cockpit controls:
- AirspeedIndicator
- AltitudeIndicator
- HeadingIndicator
- RPMIndicator
- VerticalSpeedIndicator
All of these controls use multiple graphic layers to achieve their
effects. However, the AttitudeIndicator control is, by far, the most
complex and thus was chosen for this article.
History
02/23/2015
| | Original article
|
02/25/2015
| | Modified article formatting
|