A Graphics Transform Stack
GDI+ graphics provides a great deal of power for two
dimensional manipulation of geometric shapes thanks to the availability of a
proper matrix transform system. Using a matrix, graphical objects may be
scaled, rotated and translated in any combination to give great effects such as
text displayed at any angle and geometric shapes shown in different scales and
positions. GDI+ however, is still an immediate-mode graphics system which
is to say that everything you see on screen or on the printer is generated
more-or-less at the moment that you see it, which is fine if all you want to do
is display some text and a couple of rectangles but can be restricted if you
want to have interactive graphics or any kind of animation.
Graphics systems which maintain information about geometric
shapes or graphical objects that maybe interacted with are known as
retained-mode systems because they retain information about all the
graphical objects in an object model. There are numerous ways of creating
retained-mode graphics systems and they can come in all flavors and
complexities. Indeed, you can find articles on the subject that go back
over 20 years and which present essentially the same ideas. Generally, they all follow a basic principle which
includes a tree-like structure of linked objects which have a relationship to
each other in space. Objects in the tree know how to draw themselves and
may have other abilities such as hit-testing built in.
Tree structures represent objects having a parent-child
relationship with an implication that operations performed on a parent object
will have some effect on the list of children that it owns. In the
case of retained-mode graphics systems the parent-child relationship normally
includes transformations which have been applied to the parent and which must
also affect the child in a specific manner. For example, a parent object
having one child might be rotated about its own axis by a few degrees and the
child would be expected to remain in place relative to that object but also in
the rotated position. Similarly, a parent object having one child might be
scaled to twice its normal size and so in turn the child object would also have
to appear larger in relationship to the parent.
Graphics systems which display objects in this fashion,
although based upon simple principles, can store extremely complex object-models
with very deeply nested trees of objects. All of them having this common
parent-child relationship which must be maintained at all times during a draw
cycle that must be repeatable at a reasonably high frequency. Traversing the
tree of objects requires that from any particular node in the tree you must be
able to save the current state of the graphics transform so that after all the
child nodes have been processed, the graphics state can be restored enabling any
sibling nodes on the current branch can use the same conditions for their
drawing output.
The common transforms.
A very complex graphics system can be created using just
three
simple properties applied to each object. These properties are: scale, rotation
and translation. The three properties are generally represented as follows:
-
Scale. Two values that modify the scale of an object
in the X and Y direction. A scale of 1,1 does not change the object. A scale
of 2,2 doubles it's size, 0.5,0.5 halves its size and 2,1 distorts the object
so its the same height but twice as wide.
-
Translation. Two values that move an object in
space. A translation of 100,100 moves the object 100 units across the X axis
and 100 units along the Y axis.
-
Rotation A single value that specifies the number of
degrees an object should be rotated about it's axis.
Figure one shows two simple shapes that have been
displayed using the graphics transform to position, scale and rotate them. The
code that achieved this is shown in this listing.
Note how the entire graphics transform is updated before
each figure is drawn.

Figure 1. Simple shapes positioned by transformations.
Parent-child relationships.
Situations such as that illustrated in figure one employ a
very simple graphics strategy in which the whole transform is replaced in each
time a shape needs to be drawn. The interesting thing about transforms however
is that they can be cumulative. Effectively, one transform may be added to
another, or more properly, multiplied by another, to produce a second transform
which is the sum of both of them. Consider the situation shown in figure
2.

Figure 2. A graphic object with a child object.
Figure two shows how a triangular object having a square
child-object can be manipulated. The triangle on the left has not been
rotated but the one on the right has. Notice how the child square has remained
in position relative to its parent object. An important thing to be able to do
if you want to create say, a car, with wheels, doors engine and body and draw it
in several places without having to laboriously re-position all the items that
ought to have an inherent relationship to one-another.
The structure of the object shown is interesting because the
parent child relationship is defined as follows:
To rotate the triangle and all it's children you simply
need to change the rotation of the triangle, like so:
To draw the two triangles as a single scene, the objects
can be placed together like so:
The child objects have their own transformations so a simple
tweak to the scale or rotation of one of them will produce a different picture:

Figure 3: A child object independently transformed.
Managing the stack.
There are two ways to maintain a graphics stack. The first is
using a collection such as a Stack class which enables you to push items onto
the stack and pop them off again. This is useful if you're running more than one
stack at a time but generally, the simplest method is to use the computer's own
stack mechanism to manage the operations and maintain the graphics state in the
instances of the objects being drawn. Figure 4 shows a typical
algorithm used to draw simple retained mode systems.

Figure 4.
The state of the Graphics object is maintained by using the
GraphicsState object which stores all the relevant information enabling you to
restore it after child objects have drawn. This code
shows the drawing code from a simple retained mode graphics system.
Graphics Explorer.
To illustrate the techniques described in this article the
GraphicsExplorer program presents a user interface that can be used to create
and examine a tree of objects having complex relationships to one-another. The
explorer presents a tree structure which you can add nodes to or delete nodes
from, a property grid that can be used to edit the properties of each object, a
display which shows the graphics as they are edited and a text display that
explains exactly what the system is doing for any given tree of objects. Figure
5 shows the Graphics Explorer in action.

Figure 5. The Graphics Explorer
The code for the Graphics Explorer is in the code page but
before you examine it closely remember the following points: Graphics Explorer
is an application that illustrates a concept. Not an example of how such a
system should be written. It uses TreeNode based objects as graphical objects
and so has an amount of baggage that would not make for a performant graphics
system. The application is a simple demo and has no serialization so don't use
it for creating a masterpeice that you would wish to save. The objects are very
restricted in capability for the sake of simplicity.
You can find the Source Code files here.
Return to the main
index.