> ### 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 ]