[ Exercises | Chapter Index | Main Index ]

Solution for Programmming Exercise 4.6


This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.


Exercise 4.6:

For this exercise, you will write another program based on the non-standard Mosaic class that was presented in Section 4.6. While the program does not do anything particularly interesting, it's interesting as a programming problem. The program will do the same thing as the following applet:

The program will show 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 each 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. When the rectangle gets to the outer edge of the applet, the loop ends. The animation then starts again at the beginning of the loop. 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 moves 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

/**
 * 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.  The animation continues until the user closes the window.
 * This program depends on the non-standard classes Mosaic and MosaicCanvas.
 */

public class MosaicStrobe {

   
   /**
    * Opens a mosaic window then play the "stobe" animation over and over
    * as long as the window is still open.
    */
   public static void main(String[] args) {
      Mosaic.open(41,41,5,5);
      while ( Mosaic.isOpen() ) {
          Mosaic.delay(1000);
          strobe();
      }
   }  // end main()


   /**
    * 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.
    */
   static void strobe() {

      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(200);
          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()
   

   /**
    * Draws the outline of a rectangle in the mosaic window by setting the color
    * of each little square on that rectangle. (Only the outline of the rectangle
    * is drawn; it is not filled in.)
    * @param top gives the starting row, at the top edge of the rectangle
    * @param left gives the starting column, at the left edge of the rectangle
    * @param height gives the number of rows in the rectangle
    * @param width gives the number of columns in the rectangle
    * @param red the red component of the color, in the range 0 to 255
    * @param green the green component of the color
    * @param blue the blue component of the color
    */
   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()


}  // end class MosaicStrobe

[ Exercises | Chapter Index | Main Index ]