Common Transformations
A particularly common graphic operation is to rotate or stretch a drawing around a point other than the origin. Deriving the mathematics for this operation directly would be hard but building the transformation out of simpler ones is relatively easy.
For example, suppose you want to rotate a drawing 30 degrees around the point (100, 200). You can build this transformation by first translating the point (100, 200) to the origin, rotating 30 degrees around the origin, and then translating the point that's still at the origin back to (100, 200). The following code shows how you could do this for the Graphics object
gr:
gr.TranslateTransform(-100, -200, Drawing2D.MatrixOrder.Append)
gr.RotateTransform(30, Drawing2D.MatrixOrder.Append)
gr.TranslateTransform(100, 200, Drawing2D.MatrixOrder.Append)
The
RotateAround method shown in the following code makes this type of combined transformation easier. It takes the center and angle of rotation as parameters and builds the combined transformation for you:
' Rotate around the indicated point.
Private Sub RotateAround(ByVal gr As Graphics, _
ByVal X As Single, ByVal Y As Single, ByVal degrees As Single)
' Translate to center the rectangle at the origin.
gr.TranslateTransform(-X, -Y, Drawing2D.MatrixOrder.Append)
' Rotate.
gr.RotateTransform(degrees, Drawing2D.MatrixOrder.Append)
' Translate the result back to its original position.
gr.TranslateTransform(X, Y, Drawing2D.MatrixOrder.Append)
End Sub
The following event handler code shows how a program might use this method. The code draws a rectangle, calls the
RotateAround method to rotate around the rectangle's center, and then draws the same rectangle again:
 | |
Figure 5. Rotating Around a Point: The RotateAround method makes it easy to rotate around an arbitrary point. |
' Draw the original rectangle.
e.Graphics.DrawRectangle(Pens.Red, 50, 50, 100, 100)
' Rotate 30 degrees around the rectangle's center.
RotateAround(e.Graphics, 100, 100, 30)
' Draw the rectangle.
e.Graphics.DrawRectangle(Pens.Green, 50, 50, 100, 100)
Figure 5 shows the result. Compare this to
Figure 3, which shows a rectangle rotated around the origin.
Using a similar transformation compositing technique, you can create a
ScaleAround method that translates a point to the origin, scales, and then translates the origin back to its original position. For example,
ScaleAround would let you scale an object around its center, making it bigger or smaller without moving it.
Centering Pictures
Some other useful transformations involve drawing an object inside a given area. Common ways you might want to draw the object include:
 | |
Figure 6. Sizing Content to Fit: Program FitToBoxes uses transformations to fit smiley faces into PictureBoxes in different ways. |
- Drawing the object as it is without any transformations
- Drawing the object centered in the area
- Drawing the object stretched to fill the area
- Drawing the object as large as possible within the area without stretching it
The example program
FitToBoxes (download
here), shown in
Figure 6, demonstrates each of these alternatives. The program's
DrawSmiley subroutine draws a smiley face in the rectangle -20 <= x <= 20, -20 <= y <= 20. The series of images in the program's top row shows the smiley untransformed, centered in its display area, stretched to fill the display area, and enlarged as much as possible without stretching in a relatively tall and thin display area. The bottom picture shows the smiley enlarged without stretching in a relatively short and wide display area.
Program FitToBoxes includes several functions that make using these common transformations easier. Each function returns a transformation matrix that the program can assign to a Graphics object's Transform property.
For example, the following code shows how the program draws the centered smiley face:
Dim world_rect As New RectangleF(-20, -20, 40, 40)
e.Graphics.Transform = _
CenterWorld(world_rect, PictureBox2.ClientRectangle)
DrawSmiley(e.Graphics)
This code makes a RectangleF to define the area occupied by the smiley face in its native or
world coordinate system. It calls the
CenterWorld method to build a transformation that maps the world coordinate rectangle onto the
device coordinate rectangle where the smiley face should be displayed.
CenterWorld returns a matrix representing the necessary transformation, which the code assigns to the Graphics object's
Transform property. The code then simply calls the
DrawSmiley method to draw the smiley face; the transformation centers the result.
The following code shows the
CenterWorld function. It first calculates how far from the left and top edges of the device coordinate rectangle the world coordinates must be positioned to center the result. It creates a new Matrix object and calls its
Translate method to make the Matrix represent the necessary translation. The function then returns the Matrix:
' Make a transformation that centered the world coordinate
' rectangle in the device coordinate rectangle.
Private Function CenterWorld(ByVal world_rect As RectangleF, _
ByVal device_rect As RectangleF) As Matrix
' Find the necessary left and top margins.
Dim l_margin As Integer = _
device_rect.Left - world_rect.Left + _
(device_rect.Width - world_rect.Width) \ 2
Dim t_margin As Integer = _
device_rect.Top - world_rect.Top + _
(device_rect.Height - world_rect.Height) \ 2
' Make the transformation.
Dim result As New Matrix()
result.Translate(l_margin, t_margin)
Return result
End Function
The following code shows the
StretchToFitWorld function, which returns a transformation matrix that stretches a world coordinate rectangle to fit a device coordinate rectangle. It first defines an array of PointF objects that contain the coordinates of the device rectangle's upper left, upper right, and lower left corners. It then creates a Matrix object, passing its constructor the world coordinate rectangle and the coordinate array. This version of the Matrix class's constructor automatically creates a transformation mapping the world rectangle onto the rectangle defined by the array of three points. (Note that it can also map the world rectangle onto a skewed parallelogram if the three corner points don't define a rectangle.)
' Make a transformation that maps a world coordinate
' rectangle to device coordinate rectangle.
Private Function StretchToFitWorld( _
ByVal world_rect As RectangleF, _
ByVal device_rect As RectangleF) As Matrix
Dim pts() As PointF = { _
New PointF(device_rect.Left, device_rect.Top), _
New PointF(device_rect.Right, device_rect.Top), _
New PointF(device_rect.Left, device_rect.Bottom) _
}
Return New Matrix(world_rect, pts)
End Function
The
ScaleToFitWorld method shown in the following code is the trickiest of these transformation-building methods. It starts by comparing the height-to-width aspect ratios of the world and device rectangles to determine whether the scaled world rectangle will be limited by the device rectangle's height or its width.
For example, the smiley face in the top right picture in
Figure 6 is limited by the display area's width while the picture at the bottom in
Figure 6 is limited by its display area's height.
Based on the rectangles' relative aspect ratios, the code calculates the scale factor that will make the world rectangle as tall or wide as possible.
The code creates a new Matrix object and then builds a combined transformation to map the world rectangle to an enlarged and centered position on the device rectangle. It starts by translating the world rectangle to the origin and then scaling it by the calculated scale factor.
Next the code must translate the result so it is centered in the device rectangle. It calculates the world rectangle's scaled size and determines where it must translate the rectangle to center it properly. It adds the needed translation transformation and returns the resulting Matrix object (see
Listing 1).
These transformation functions are particularly useful when printing. Depending on your application, you might want to center a picture on the printed page. You might also want to enlarge the picture to fit the page, either with or without stretching it. The
CenterWorld,
StretchToFitWorld, and
ScaleToFitWorld functions not only make these operations easy, but also let you center or fit an image to a specific portion of the page so you can add other graphics and text around it.