COMP 310
|
Lec11: Factories, cont. & Transforming How We Paint |
Is updating the state of the ball the only place where strategies might be useful? Take a look at the Fishworld Demo.
How does Fishworld create fish that are not only the correct size, but are always swimming forward? How does it create different sizes and orientations of the same image?
Remember that a fish (ball) knows where it is and which way it is going because it has a position and a velocity. It has a radius too, so it knows its size as well. Thus any given ball can take a common representation of an image, e.g. a fish, scale it as per its radius, rotate it as per its velocity and translate it as per its position and end up with an image that is always oriented correctly with respect to its movement. The mathematical process of scaling, rotating and translating is called an "affine transform".
An affine transform is the mathematical process of scaling (making larger or smaller), rotating (pivoting around the origin) and translating (moving a specified distance and direction) of a point in space. An affine transform is a single operation that does all three transformations. Remember that a point (coordinate in space, e.g. java.awt.Point) is really a vector from the origin, so a affine transformation technically transforms vectors, not geometrical points.
Affine transforms may seem mathematically daunting, but they are actually quite straightforward. Plus, Java provides an AffineTransform class that encapsulates the mathematics and makes dealing with affine transformation very easy. Translation: we never have to fuss with the math, we just use the results. Gotta love that!
Example of the operations involved in a single Affine transformation, showing the individual steps of rotating, scaling and translating:
Original vector: then Rotated vector:
then Scaled vector and then Translated vector:
Note that the order of operation is important:
Original vector: then Rotated vector: then Translated vector:
as compared to
Original vector: then Translated vector: then Rotated vector:
In fancy mathematical words, we same that rotation and translation are non-commutative. A net Affine transformation is thus dependent on not only what operations (rotate, scale, translate) are defined, but also in what order those operations are defined.
So, what all this boils down to is the following: In order to put an visual representation of a ball onto the screen, we need to know 3 things:
Property we need: | Where we get it: |
1. The scaling of the ball with respect to some "unit" size | The ball's radius |
2. The rotational orientation of the ball | The direction of the ball's velocity |
3. Translation from the origin of the display component | The ball's x-y location. |
With the above 3 things, we can set up an AffineTransform object and it will do all the math to get our ball's visual representation to the correct location on the screen, with the correct size and with the correct orientation!
But what is this "visual representation"? Notice a key phrase in the above table: "with respect to some 'unit' size".
All we need to do is to define a unit-sized visual representation of the ball and the affine transform process will take care of sizing it up, orienting it and locating it on the screen.
This unit sized representation is what is called a "prototype" representation of the ball. We are implementing what is known as the Prototype Design Pattern. Prototypes are another way in which we can look at the creational process of objects.
Prototypes are objects that are used to as a templates with which to create the actual objects to be used.
So what we will do is to define a unit-sized prototype that the affine transform will use to create a full-sized, properly located and oriented visual representation of the ball.
Luckily, Java provides one interface and one classe that make our lives much easier in this regards (though it would have been even easier if the two were abstractly related!):
java.awt.Shape -- An interface that represents a shape as a set of connected points. There are many supplied implementations of Shape , including and not limted to, lines, arcs, ellipses, rectangles, polygons, etc. The points that define these shapes are defined on a double-valued x-y coordinate system. Thus the unit size (distance) is defined to be 1.0.
java.awt.Image -- A class that represents an image, such as one from a GIF, JPG or PNG file. Due to the interger coordinate system used to locate pixels in an image, Image objects need to have their unit size defined as the average of the width and height of the image in pixels. Furthermore, since the useful image may not completely fill the rectangular image area, a "fill factor" needs to be specified that gives the ration of the size of the useful part of the image to the entire image. A fill factor is necessarily always a values less than 1.0.
AffineTransform objects can be used to transform both Shape and Image objects.
© 2011 by Stephen Wong and Scott Rixner