java.awt.geom.AffineTransform is a class that represents a PostScript-like coordinate
transformation i.e. a 3 × 3 matrix multiplication. It lets you rotate around
an anchor point, translate the axes and shear (change the x and y scaling). Using negative scale factors, you can
flip (reflect around the x or y axis). The transform converts an x,y coordinate to a new x',y'
coordinate.
[ x'] [ m00 m01 m02 ] [ x ] [ m00 * x + m01 * y + m02 ]
[ y'] = [ m10 m11 m12 ] [ y ] = [ m10 * x + m11 * y + m12 ]
[ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
Though AffineTransform is often associated with drawing, it is not tied in any way to
the Graphics class. It is purely mathematical.
Tips
- Watch out: setToRotation is not cumulative. Graphics2.setTransform replaces the transform. It does not append
the transform to the existing transforms.
- It pays to learn how to use Graphics2D and AffineTransform. It is much simpler to draw something simple on a simple grid and transform it
than it is to compute all the transformed points on the natural grid yourself.
- Angles are in radians
- Points are in the current transformed user coordinate system. Best to just experiment drawing lines on a
Canvas till you get the hang of it before tackling your real project.
- Graphics.drawImage
will do a simple scale for you, often inadvertently. You don’t need an AffineTransform for that.
Manual Use of AffineTransform
The basic idea is this. A transform maps every point x,y onto a new point, by a combination of rotation,
scaling, translation and mirroring. You can manually transform a point like this:
Drawing with AffineTransform
You can also simply plug an AffineTransform into your Graphics2D object. Then you can specify simple coordinates and the drawing will appear in
transformed coordinates. Note that with Graphics2D, your coordinates are
doubles not ints.
Correcting Mouse Co-ordinates
The catch is, the mouse knows nothing about your transforms. You need to convert mouse screen coordinates to
your user-coordinates. This is the inverse of what you normally do, namely converting convenient user
coordinates to pixel display coordinates. For this, you need the inverse of your transform.
You can learn more with a Google
search.
Rotating Images
Rotating Images is a tad trickier than you might expect. The code snippet below will show you how it is done.
The tricks:
- AffineTransform has no flip method to turn an
Image upside down. Use scale( 1.0, -1.0 ) instead.
- AffineTransform has no mirror method to reverse an
image left to right. Use scale( -1.0,
1.0 ) instead.
- the Y axis grows down. This in confusing if you are familiar with Cartesian coordinates that mathematics uses.
- If you are rotating by multiples of 90 degrees, you will get faster and more accurate results if you use
quadrantRotate().
- If you scale or rotate, the Image will spin around the upper left corner of the
Image and disappear off the screen. You must first move the origin to spot you want
to rotate around, the image, do your rotation, then move the origin to some more suitable location. You will
need some translation before the rotation, after or both. The corrections you need to move the origin after the
rotate cause brain ache. It is not immediately obvious whether you want negative or positive translations and
whether you should apply them in the x or y direction. Any error results in a blank image. If you want to solve
the problem by brute force, you can try all 32 combinations of rotation direction, positive or negative
correction and whether you apply in the x or y direction. And that does not even count the magnitude of the
correction. Imagine you had a little X made of wire with x and y axes labeled that you could plop down over
drawing at the origin. Imagine spinning the X in clockwise or counter-clockwise direction. Then make your
origin correction in terms of the directions on the arms of the spinned X rather than in terms of the final
image. Making such an X out of a paper clip and with coloured tip made with a Sharpie, that you place over your paper drawings will help immensely to grok the
notion of rotating the axes.
- A positive translate moves the origin right and down. Think of translate as moving the origin, not moving
objects.
- A positive rotate() rotates the clockwise around the origin, and negative
counter clockwise. quadrantRotate() measures in 90
degree increments. rotate() measures in radians.
- Try writing yourself a training program that just draws coloured asymmetrical shapes. Experiment with
various combinations of transforms until you see the effects, stage by stage.
- When you finally get around to drawing something in your virtual coordinate system, logically the various
transforms are computed starting with the most recently applied working back to the device coordinate
system.
- Beware of transform(). It applies transforms out of order.
Rotating is trickier than you might suppose. Run this program and follow through the steps to make sure you can
follow what happens at each stage.
Learning More
Oracle’s Technote Guide on
2D : available:
Oracle’s Javadoc on
AffineTransform class : available:
Oracle’s Javadoc on
Graphics2D class : available: