Solution for
Programming Exercise 4.6
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 4.6:
For this exercise, you will write a program that has the same behavior
as the following applet. Your program will be based on the non-standard Mosaic
class, which was described in Section 4.6.
(Unfortunately, the applet doesn't look too good in many versions
of Java.)
The applet shows a rectangle that grows from the center of the
applet to the edges, getting brighter as it grows. The rectangle is
made up of the little squares of the mosaic. You should first
write a subroutine that draws a rectangle on a Mosaic window.
More specifically, write a subroutine named rectangle such that
the subroutine call statement
rectangle(top,left,height,width,r,g,b);
will call Mosaic.setColor(row,col,r,g,b) for each little square
that lies on the outline of a rectangle. The topmost row of the rectangle
is specified by top. The number of rows in the rectangle is
specified by height (so the bottommost row is top+height-1).
The leftmost column of the rectangle is specified by left. The
number of columns in the rectangle is specified by width
(so the rightmost column is left+width-1.)
The animation loops through the same sequence of steps over and over.
In one step, a rectangle is drawn in gray (that is, with all three color
components having the same value). There is a pause of 200 milliseconds
so the user can see the rectangle. Then the very same rectangle is
drawn in black, effectively erasing the gray rectangle. Finally, the
variables giving the top row, left column, size, and color level of the
rectangle are adjusted to get ready for the next step. In the applet,
the color level starts at 50 and increases by 10 after each step.
You might want to make a subroutine that does one loop through
all the steps of the animation.
The main() routine simply opens a Mosaic window and then
does the animation loop over and over until the user closes the window.
There is a 1000 millisecond delay between one animation loop and the next.
Use a Mosaic window that has 41 rows and 41 columns. (I advise you
not to used named constants for the numbers of rows
and columns, since the problem is complicated enough already.)
Discussion
According to the exercise, the first task is to write a subroutine
that draws a rectangle in a Mosaic window. The name and seven parameters
of the subroutine are specified. So the first line of the subroutine
definition will look like this:
static void rectangle(int left, int top,
int height, int width, int r, int g, int b)
The subroutine has to draw the four lines that make up the outline of
the rectangle. Each square that lies along one of these lines will
have its color set by a call to Mosaic.setColor(row,col,r,g,b).
We just have to make sure that row and col take on
all the correct values that are needed to hit all the necessary squares.
For the topmost line of the rectangle, for example, row is
given by the value of the parameter, top. And, as the exercise
explains, the value of col varies from left to
left+width-1. So the topmost line of the rectangle can be
drawn with the for loop:
for ( col = left; col <= left+width-1; col++ )
Mosaic.setColor(top,col,r,g,b);
The bottommost row can be drawn by a similar for loop, except
that the value of row along the bottommost row is top+height-1,
as noted in the exercise. We can combine the two for loops to
draw the top and bottom lines at the same time:
for ( col = left; col <= left+width-1; col++ ) {
Mosaic.setColor(top,col,r,g,b); // A square on the top line.
Mosaic.setColor(top+height-1,col,r,g,b); // A square on the bottom line.
}
Drawing the leftmost and rightmost lines of the rectangle is similar.
The row number along these lines varies from top to
top+height-1. The column number of the leftmost line is given
by left, and the column number of the rightmost line is
left+width-1. So, these two lines can be drawn with the for
loop:
for ( row = top; row <= top+height-1; row++ ) {
Mosaic.setColor(row,left,r,g,b); // A square on the left line.
Mosaic.setColor(row,left+width-1,r,g,b); // A square on the right line.
}
When I wrote my program, I used the test "row < top+height"
in the first for loop in place of "row <= top+height-1",
and similarly for the second loop. The meaning is the same, and the form that I
used would probably be preferred by most Java and C++ programmers.
Putting this all together gives the rectangle subroutine:
static void rectangle(int top, int left, int height, int width, int r, int g, int b) {
int row, col;
for ( row = top; row < top + height; row++ ) {
Mosaic.setColor(row, left, r, g, b);
Mosaic.setColor(row, left + width - 1, r, g, b);
}
for ( col = left; col < left + width; col++ ) {
Mosaic.setColor(top, col, r, g, b);
Mosaic.setColor(top + height - 1, col, r, g, b);
}
} // end rectangle()
We still have the problem of designing the complete program. The main()
routine plays the same animation over and over as long as the window is still open.
A pseudocode algorithm for this is given by
Open a mosaic window
while the window remains open:
Delay for 1000 milliseconds
Play once through the animation
I will write a subroutine named strobe that plays the animation once.
Using this subroutine, the body of the main() routine becomes
Mosaic.open(41,41,5,5);
while ( Mosaic.isOpen() ) {
Mosaic.delay(1000);
strobe();
}
The final stage in the design is to write the strobe() routine. The
outline of an algorithm is already given in the exercise. It can be written more
formally as
Initialize variables top,left,size,brightness for the first step
Repeat until the rectangle is as big as the whole window:
Draw the rectangle in gray
Delay 200 milliseconds
Draw the rectangle in black
Update the variables for the next step
The window has 41 columns and 41 rows of squares. We want the rectangle
to start at the middle of the window. That will be in row 20 and column 20,
so we can initialize top and left to 20. The rectangle
starts off as small as possible, that is with size equal to 1. The
value of size is used as both the width and the height of the
rectangle. The brightness is initialized to 50. The value of
brightness is used for each of the color components, r,
g, and b. So, the rectangle can be drawn in gray with
the subroutine call statement:
rectangle(top,left,size,size,brightness,brightness,brightness);
To draw the rectangle in black, just substitute 0 for brightness in
this statement.
To go from one step in the animation to the next, brightness increases by 10.
The topmost line of the rectangle move up one row. This means that the
value of top decreases by 1. Similarly, the value of left
decreases by 1. The rectangle grows by one row on each side,
so its size increases by 2. There are several ways to check whether
the animation should continue. We could check whether its size is <= 41.
Or whether left is >= 0. Or whether top is >= 0.
Alternatively, we could notice that there are 21 steps in the animation,
and we could just use a for loop to count from 1 to 21. Picking one
of these methods more or less at random, the algorithm for the strobe()
becomes
left = 20
top = 20
size = 1
brightness = 50
while left is >= 0:
draw the rectangle with specified brightness
delay 200 milliseconds
draw the rectangle in black
left -= 1
top -= 1
size += 2
brightness += 10
This translates easily into Java code. One more note: In my implementation
of the subroutine, I changed the condition in the loop to
"while (left >= 0 && Mosaic.isOpen())",
since there is no reason to continue with the animation if the user has
closed the window. However, the program will work without this extra test.
It just might take an extra second or so for the program to end after the
user closes the window.
The Solution
public class MosaicStrobe {
/* This program shows an animation in which a small square grows
from the center of the window until it fills the whole square.
Then, after a short delay, the process repeats. As the square grows,
its brightness also increases. This program depends on the
non-standard classes Mosaic and MosaicCanvas.
*/
public static void main(String[] args) {
// Open a Mosaic window, then show the "strobe" animation over
// and over until the user closes the window.
Mosaic.open(41,41,5,5);
while ( Mosaic.isOpen() ) {
Mosaic.delay(1000);
strobe();
}
} // end main()
static void strobe() {
// Draw the animation, showing a square that starts at
// the center of the mosaic and grows to fill the whole window.
// The square gets brighter as it grows. Note that the
// animation ends immediately if the user closes the window.
int rectSize; // The number of rows (and columns) in the square.
int left; // The leftmost column in the square.
int top; // The topmost row in the square.
int brightness; // The brightness of the square, which increases from
// 50 to a maximum of 250 as the square grows. This
// quantity is used for all three color components,
// giving a gray color that brightens to white.
left = 20; // Start at the center of the 41-by-41 mosaic.
top = 20;
rectSize = 1;
brightness = 50;
while (left >= 0 && Mosaic.isOpen()) {
// Draw the square in gray, pause so the user can see it, then
// redraw the same rectangle in black, effectively erasing the
// gray square.
rectangle(top,left,rectSize,rectSize,brightness,brightness,brightness);
Mosaic.delay(50);
rectangle(top,left,rectSize,rectSize,0,0,0);
// Now, adjust the parameters to get ready to draw the next square
left--;
top--;
rectSize += 2;
brightness += 10;
}
} // end strobe()
static void rectangle(int top, int left, int height, int width, int r, int g, int b) {
// Draws the outline of a rectangle in the mosaic window by setting
// the color of each little square on that rectangle; top gives the
// starting row of the rectangle; left gives the starting column;
// height gives the number of rows in the rectangle; width is the
// number of columns. The red, green, and blue components of the
// color are specified by r, g, and b.
int row, col;
for ( row = top; row < top + height; row++ ) {
Mosaic.setColor(row, left, r, g, b);
Mosaic.setColor(row, left + width - 1, r, g, b);
}
for ( col = left; col < left + width; col++ ) {
Mosaic.setColor(top, col, r, g, b);
Mosaic.setColor(top + height - 1, col, r, g, b);
}
} // end rectangle()
} // end class MosaicStrobe
[ Exercises
| Chapter Index
| Main Index
]