In Depth Banner
Skip Navigation Links

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:

Triangle size 100,100 
    Scale 1,1
    Translation 100,100
    Rotation 0
        Square size 10,10
        Scale 1,1
        Translation 0,-50
        Rotation 0
 

To rotate the triangle and all it's children you simply need to change the rotation of the triangle, like so:

 

Triangle size 100,100
    Scale 1,1
    Translation 100,100
    Rotation 45
        Square size 10,10
	Scale 1,1
        Translation 0,-50
        Rotation 0

 

To draw the two triangles as a single scene, the objects can be placed together like so:

 

Triangle size 100,100
    Scale 1,1
    Translation 100,100
    Rotation 0
        Square size 10,10
        Scale 1,1
        Translation 0,-50
        Rotation 0
Triangle size 100,100
    Scale 1,1
    Translation 300,100
    Rotation 45
        Square size 10,10
        Scale 1,1
        Translation 0,-50
        Rotation 0

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:

Triangle size 100,100
    Scale 1,1
    Translation 100,100
    Rotation 0
        Square size 10,10
        Scale 1,1
        Translation 0,-50
        Rotation 0
Triangle size 100,100
    Scale 1,1
    Translation 300,100
    Rotation 45
        Square size 10,10
        Scale 3,3
        Translation 0,-50
        Rotation -30

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.

Copyright © Bob Powell 2003-2009. All rights reserved