As we discussed earlier when we considered the Java 2D coordinate system, the java.awt.geom.AffineTransform class represents a general mapping from one coordinate system to another. AffineTransform defines a general coordinate-system transformation that can include translation, scaling, rotation, and shearing.
One of the easiest ways to obtain an AffineTransform object is to use one of the static methods defined by AffineTransform. For example, getScaleInstance() returns an instance of AffineTransform that represents a simple scaling transformation.
Another way to get an AffineTransform is with the AffineTransform() constructor, of course. The no-argument version of the constructor returns an AffineTransform that represents the identity transform--that is, no transform at all. You can modify this empty transform with a number of methods. Note that AffineTransform defines several other constructors, but we have to wait to discuss them until after we've discussed the mathematics that underlie AffineTransform.
Once you have obtained an AffineTransform object, you can modify it with methods just like the methods defined by Graphics2D. Each of the translate(), scale(), rotate(), and shear() methods modifies an AffineTransform by adding the specified transformation to it. Note that there are two versions of rotate(). One rotates around the origin and the other rotates around a specified point; both use angles specified in radians. Remember that calls to these four methods are cumulative: you can build up a complex transformation as a combination of translation, scaling, rotation, and shearing.
AffineTransform also defines noncumulative methods. setToTranslation(), setToScale(), setToRotation(), and setToShear() set an AffineTransform to a single transform, replacing whatever transform was previously contained by the AffineTransform.
Once you have created and initialized an AffineTransform object, you can use it to transform points and shapes. AffineTransform defines a number of transform() methods that transform points represented by either java.awt. geom.Point2D objects or arrays of numbers. deltaTransform() is a variant of transform() that performs a transformation disregarding any translation component. It is designed for transforming distances or position-independent vectors, instead of actual points. inverseTransform() is the inverse of transform()--it converts points expressed in the new coordinate system back to the corresponding points in the original coordinate system.
The transform(), deltaTransform(), and inverseTransform() methods are fairly low-level and typically are not used directly by Java 2D programs. Instead, a program typically uses the createTransformedShape() method, which provides a powerful, high-level transformation capability. Given an arbitrary Shape object, this method returns a new Shape that has been transformed as specified by the AffineTransform object.
The coordinate system transformations described by AffineTransform have two very important properties:
Straight lines remain straight
Parallel lines remain parallel
An AffineTransform is a linear transform, so
the transformation can be expressed in the matrix notation of
linear algebra. An arbitrary AffineTransform
can be mathematically expressed by six numbers arranged in a
matrix like this:
In this matrix, tx and ty
are the translation amounts,
sx and sy are the scaling
factors, and shx
and shy are the shearing factors, all in the
X and Y dimensions, respectively.
As we'll see in a moment, rotation is a combination of
scaling and
shearing, so there are not separate
rx and ry numbers.
To transform a point from one coordinate system to another using
an AffineTransform, we multiply the point by
this matrix. Using matrix notation (and adding a few dummy
matrix elements), the equation looks like this:
This matrix equation is simply shorthand for the following
system of equations:
x' = sx*x + shx*y + tx y' = shy*x + sy*y + ty
The identity transform does not perform any
transformation at all. It looks like this:
Mathematically, rotation is a
combination of scaling and shearing. The rotation of an angle
theta around the origin is expressed with a
matrix like this:
You don't need to understand how this rotation matrix works. If
you remember basic trigonometry, however, you can use it
and the preceding equations to verify that this matrix works for the
base cases of 90-degree and 180-degree rotations.
As we've seen, it is possible to make cumulative changes to an
AffineTransform. This is done by multiplying
the current transformation matrix by the new transformation
matrix. For example, suppose we perform a translation by
100 units in both the X and Y dimensions and follow this by
scaling both the X and Y dimensions by a factor of 2. The
resulting AffineTransform matrix is the
product of the two individual matrices:
Note that matrix multiplication is not commutative. If we
perform the scaling operation first and then do the
translation, we obtain a different result:
Most applications do not have to work with matrices explicitly in order to perform coordinate-system transformations. As we've seen, it typically is easier to use the translate(), scale(), rotate(), and shear() methods of either AffineTransform or Graphics2D. It is useful to understand the mathematics underlying AffineTransform, however.
You may, on occasion, have the need to create a
custom AffineTransform object from a set of
six numbers. A number of AffineTransform
constructors and methods take matrix elements as arguments.
These matrix elements are either passed in explicitly or
specified in an array. Note that the matrix-element naming
system used by the AffineTransform class is
different than the system I've used here. The parameter names
for AffineTransform methods are based on the
following matrix:
This is nothing more that a different naming scheme for the
elements we are already familiar with:
When matrix elements are passed to or returned by an
AffineTransform in an array of
float or double values,
they are stored in this order:
This corresponds to the following order using our mnemonic names:
Copyright © 2001 O'Reilly & Associates. All rights reserved.