CPSC 124, Fall 2005
Lab 13: Two-Dimensional Arrays

For this week's lab, you will be using a couple of two-dimensional arrays to help create a program that works something like this applet:

You can also run the program as a stand-alone application using the jar file VectorDrift.jar; you can find a copy of this file in the directory /classes/f05/cs124. As you can see, the program also uses animation, and this lab will also show how to write simple animations of this sort. The program that you will write is actually rather short, and you will probably be able to complete it during the lab period.

The program will use two two-dimensional arrays of double. Remember that the type for such an array is double[][], and the array is created with a constructor of the form new double[ROWS][COLUMNS]. In several places in the program, you will use a nested pair of for loops to process all the elements in a two-dimensional array.

About the Program

Before you try to program anything, you need to understand something about how the program works. The picture drawn by the program consists of a "grid" of line segments. Each line segment extends from a "base point" (x1,y1) to a "terminal point" (x2,y2). The base points of all the segments are arranged in a perfectly regular grid of rows and columns. In fact, the x-values of the base points are given by 5, 25, 45, 65, ... That is, the first column is at x=5, and each column is separated from the next by 20 pixels. The y-values and rows work in exactly the same way. Thus, if row is the row number and col is the column number, then the base point (x1,y1) can be computed as:

              x1  =  5 + 20*col;
              y1  =  5 + 20*row;

If you shift-click on the applet, all the lines will be shorter, and it will be much easier to see the grid.

Each terminal point (x2,y2) rotates about the corresponding base point (x1,y1). The speed of rotation is randomly selected and is different for each line segment. Furthermore, from time to time, the speed of rotation changes to a new random value. To determine the point (x2,y2), you only need to know the angle of the line segment (and use a little trigonometry). If the angle is a and the length of the line segment is length, then (x2,y2) is given by:

               x2  =  x1 + length * Math.cos(a);
               y2  =  y1 + length * Math.sin(a);

The program also responds to mouse motions. Every time a mouse motion event is generated, all the lines point towards the mouse position. Making the line segment point in a particular direction is just a matter of setting the angle of the line to the correct value. If (x1,y1) is the base point of the line segment and (mouseX,mouseY) is the location of the mouse, then the angle should be set to:

              Math.atan2( mouseY - y1, mouseX - x1 );

Finally, there is a color animation that gradually morphs the color of the line segments through the colors of the rainbow. The animation uses "HSB" colors, which are specified by hue, saturation, and brightness. In this case, the saturation and brightness have their maximum possible value, 1. The hue grows gradually from 0 to 1. When it surpasses 1, the value goes back to zero. In Java, HSB colors are created using a static method Color.getHSBColor(h,s,b), which takes parameters of type float. If hue is a variable of type double that specifies the desired hue, then the color used for drawing the lines would be:

              Color.getHSBColor( (float)hue, 1.0F, 1.0F );

Finally, there is the question of how to do animation. Fortunately, it's relatively easy in Java. You just have to use a "timer" that generates events periodically. A timer is an object belonging to the class Timer. A timer generates events of type ActionEvent, and requires an ActionListener to respond to the events. The actual code that implements the animation goes in the actionPerformed method of the ActionListener. A timer has a specified delay time, which tells the time between events. The delay time and the ActionListener are parameters to the Timer constructor. A timer can also have an initial delay, which tells how long before the first event is generated. The timer doesn't actually start running when it is created; it has a start() method to tell it to start running. You don't have to understand all this entirely. In this program, you can just use the main class as an ActionListener and include the following code in the constructor:

        Timer timer = new Timer(50,this);  // 50 milliseconds between events
        timer.setInitialDelay(1500);       // 1.5 seconds until first event
        timer.start();                     // Start the timer running

Vector Drift

There are no starter files for this lab. You are going to write the entire program, though you might find it useful to copy-and-paste some code from old projects. You should still begin by creating a new project in Eclipse. I suggest that you complete the lab in a series of phases...

Phase 1: General Outline. You will need a sub-class of JPanel to represent the display area of the program. Start by creating this class. You will need certain import statements at the top of the file. You don't necessarily have to remember exactly what they are; you can copy them from an old project (such as from your VideoPokerPanel.java from Lab 11). To run your program as an application, you will also need a main program. You can get this by copying from an old project, such as NotVideoPokerApplication.java from Lab 11. Modify the main program so it uses your JPanel class for the content pane of the window. You should also change the window title. The main program assumes that the JPanel has a "preferred size" that says how big it wants to be. Add a constructor to your JPanel class, and give it a preferred size, using, for example,

             setPreferredSize( new Dimension(600,460) );

This assumes that your grid of line segments, like mine, will have 30 columns and 23 rows, with 20-pixel spacing. You can also set a background color for the panel. You should now be able to run the main program as an application, but you will just get an empty window.

Phase 2: Add the vectors.. You can compute the base points of the line segments whenever you need them, but the angles must be stored in an array. The array has to be an instance variable, since it will be used throughout the file. You will need a two dimensional array of double values. The number of rows and columns in the array is the same as the number of rows and columns in the grid of line segments. In the constructor, fill the array with random values. Since Java measures angles in radians rather than degrees, the angle should be between 0 and 2*Math.PI. To get a random angle in this range, you can use

              2 * Math.PI * Math.random()

In the constructor, you should fill the array with randomly chosen angles. To draw the lines, you will need to add a paintComponent method to your class. In this method, draw each line segment, computing the base point (x1,y1) and the terminal point (x2,y2) as indicated above. Don't forget to set a drawing color, so that you are not drawing black-on-black! Also, don't forget to call super.paintComponent(g). Again, you can look back to some of your old projects for help, if you need it. If you run the program now, you should see a grid of randomly oriented line segments.

Phase 3: Add Animation. For the animation, you will need a second array to store the rotation speed of each line segment. Each spot in the array should be initialized to a small random value. In my program, I use 0.2*Math.random()-0.1, which gives a speed in the range -0.1 to 0.1. Add the Timer code as given above to the constructor of your class. Make your class into an ActionListener (by adding "implements ActionListener" to the declaration of your class and creating an actionPerformed method). The actionPerformed method is responsible for showing the next frame in the animation. To do this, you have to adjust the angle for each line segment by adding the speed value for that line segment to the angle. Then call repaint() to change the picture. If you run the program now, you should see rotating line segments, with each segment rotating at a different rate.

Phase 4: Respond to Mouse Motions. To make the line segments respond when the user moves the mouse, your class must "implement MouseMotionListener" and define the methods that are specified by this interface, mouseMoved and mouseDragged. In the mouseMoved method, you should adjust all the angles to point at the mouse position, using the "atan2" formula given above. And don't forget to call repaint(). (If you like, you can do the same thing in mouseDragged.) If you run the program now, it should respond to mouse motions.

Phase 5: Finishing Touches. To finish the program, you should make the rotation speeds change at random occasionally, and you should implement the color animation. Color animation is easy: Use an instance variable named hue of type double. In the actionPerformed method, add a small number (such as 0.002) to hue, and if the value exceeds 1, reset the value back to 0. In the paintComponent method, do

               g.setColor( Color.getHSBColor((float)hue, 1.0F, 1.0F) );

before drawing the lines. Changing the rotation speeds just means putting a new random value in the speed array. You can use a test such as

               if ( Math.random() < 0.01 )

so that there is only a small chance of making the change. This can also be done in the actionPerformed method.

And that's it! Your program should now be complete. Add comments to your program, and turn it in next Tuesday.

If you would like to add your program to your web site as an applet, you will need to create an applet version of the program. To do this, you can create a simple applet class, which can look like this (assuming that the name of your JPanel class is VectorDriftPanel):

      import javax.swing.*;

      public class VectorDriftApplet extends JApplet {

         public void init() {
            setContentPane( new VectorDriftPanel() );


David J. Eck, November 2005