CS46A Lab

Drawing Shapes

Copyright Cay S. Horstmann 2009, 2012 Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.

See lab 1 for reporting instructions.

The steps tagged with this icon are optional. Do them if you have time and want to practice more.

  1. Instance Variables vs. Local Variables
    1. This is a paper-and pencil exercise that you do with your buddy. If you found yourself trying to solve homework problems by random tinkering, you may appreciate that there must be a better way. Research shows that students do best in programming when they develop a mental model how the computer works. This exercise will give you a reliable mental model for instance variables and local variables.

      In this exercise, you and your buddy will simulate this Student class. Today's scribe will “be” the Student object. Today's driver will “be” the addQuiz method.

      Scribe: Draw a diagram of the Student object on a sheet of paper, like this:

      Get out a sheet of paper and draw a table that lists the instance variables of a Student object, like this:

      name totalScore quizCount
      Fred 0 0

      Driver: Get out a sheet of paper and draw a table of the parameter and local variables of the addQuiz method.

      score newTotalScore newQuizCount
    2. Scribe: Keep these instructions on the screen so that you can give directions to the driver.

      Driver: Have the code of the Student class on the screen so that you can refer to it.

      Scribe: Come up with a random number between 1 and 10.

      Driver: Call the addQuiz method with that number, starting as follows:

      • Fill the number into the score column
      • Look at the first line of code in the method
      • When you compute the right hand side score + totalScore, you know what score is. But you don't know what totalScore is—it is not one of your local variables. It is an instance variable. So you need to ask the object. Ask the scribe: What is totalScore?

      Scribe: Tell the driver.

      Driver: Compute score + totalScore and write the value into the newTotalScore column.

      Driver: Fill in newQuizCount in the same way. Remember to ask the scribe for any value that is not a local variable of your method.

      Driver: Move on to the next statement in the method. It asks to update an instance variable. You need to tell the object. Tell the scribe: Update totalScore to ... (whatever value is in your newTotalScore box.)

      Scribe: Update the instance variable as instructed. Cross out the old value and write the new value below it.

      Driver: Repeat with the last statement in the method.

      Driver: (very important). The method now exits. All local variables are gone. Cross out all values in the boxes. Or, if you really want to simulate the behavior perfectly, rip up the local variables sheet and draw another one for the next method call.

      Scribe: The object still exists. DO NOT cross out any values.

    3. Repeat step 2.
    4. Driver: Look at the code of getAverageScore. It has no parameters. It uses two instance variables. Ask the scribe for their values. Compute the value that the method returns and tell the scribe.

      Scribe: Record that answer in the lab report. Was it the average of the quiz scores?

    5. From your observations, both of you should formulate a rule when a variable should be a local variable and when it should be an instance variable. (For example, why is totalScore not a local variable? Why is newTotalScore not an instance variable?) Write that rule into your lab report.
  2. Drawing Circles
    1. Make a new BlueJ project. Call it twoshapes. Add two classes RectangleViewer and RectangleComponent and paste in RectangleViewer.java and RectangleComponent.java. These are from your textbook. The viewer class is plumbing, and the only thing you need to note is that it adds a RectangleComponent to the frame. The action is in the paintComponent method of the RectangleComponent. Note how two that method constructs and draws two rectangles.
    2. Compile both classes. Run the program by right-clicking on the RectangleViewer class and selecting main from the menu. What happens?
    3. Now replace the Rectangle constructor with a call to the Ellipse2D.Double constructor. Remember to import java.awt.geom.Ellipse2D. Compile. What happens?
    4. It is kind of lame that the Ellipse2D.Double class doesn't have a translate method. Just make another Ellipse2D object with (x, y) = (20, 35) and the same width and height as the first one, and draw that. What is the code of your paintComponent method now? Clean up the comments before you paste the code into your lab report.

    5. Now turn both ellipses into circles. How did you do this? (No, there is no Circle class...)

    6. Now make one circle twice as large? What is your code?

    7. Now move the smaller circle inside the larger one, so that they have the same center. This is pretty nasty because you need to specify the top left corner of the circle and not the center. I had to (gasp) get out a piece of paper and figure out (a) What is the center of the big circle? (b) If I want that center for the little circle, what is its top left corner?

      What change did you have to make in your program?

    8. It is rather of irritating to have two circles in a frame with title “Two rectangles”. How do you fix the title?

  3. Drawing a Bulls Eye
    1. We want to draw a “bulls eye” target, such as this one:

      Make a new BlueJ project bullseye. Make two classes BullsEyeViewer and BullsEyeComponent. Copy over the code from your RectangleViewer and RectangleComponent classes. After copying, remember to rename the classes in the code. Also rename the frame title to "Bulls eye". Compile and run to see that you did this right. Of course, you won't get a bulls eye quite yet—the drawing is still the same.

      What is the code of your BullsEyeViewer class?

    2. Change the draw call to fill in the smaller circle, so that you get this output:

    3. How do you change the color to red? Look it up in your text book.

    4. There is no class Ring for drawing the blue ring. Here is the plan. (a) Fill a big blue circle (b) Fill a smaller white circle inside (c) Fill the red circle inside the white circle.

      First do a red circle inside a white circle. What is the code of your paintComponent method now?

    5. Now add the blue circle. What change did you make to your code?

    6. If you have time on your hands, add two lines, like this:

      Don't fuss with numbers. Instead, use the handy getCenterX, getMinX, getMaxX, getCenterY, getMinY, getMaxY methods of the Ellipse2D.Double class, like this:

      Line2D.Double line1 = new Line2D.Double(
         circle3.getCenterX(), circle3.getMinY() - 5, 
         circle3.getCenterX(), circle3.getMaxY() + 5);

      That is, the x-coordinates of the line are the center of the circle. The y-coordinates run from 5 less than the minimum of the circle to 5 more than the maximum. Do the same with the horizontal line, and remember to draw both in black.

  4. Drawing Many Bulls Eyes
    1. If it is good to have one bulls eye, it is even better to have lots of them.

      Make a new class BullsEye and start it with this code.

      import java.awt.Graphics2D;
      public class BullsEye
          * Constructs a bulls eye with a given center.
          * @param centerX the x coordinate of the center
          * @param centerY the y coordinate of the center
         public BullsEye(int centerX, int centerY)
             x = centerX;
             y = centerY;
          * Draws the bulls eye
          * @param g2 the graphics object on which to draw this bulls eye
         public void draw(Graphics2D g2)
            g2.drawString("Bulls eye", x, y); 
         private int x;
         private int y;
    2. Change the paintComponent method of the BullsEyeComponent to
      Graphics2D g2 = (Graphics2D) g;
      BullsEye be1 = new BullsEye(100, 100);
      BullsEye be2 = new BullsEye(200, 200);
      BullsEye be3 = new BullsEye(50, 300);

      Don't erase the old drawing code, though. You'll need it in the next step. Comment it out by enclosing it in /* */.

      Run the program. Why does it print three strings?

    3. That string trick is useful for checking that the positioning works, but we actually want three bulls eyes. Paste the bulls eye drawing instructions into the draw method. (Don't erase the string drawing yet.) What happens?
    4. Of course, our original code drew one bulls eye at a particular place. You need to change it so that it draws the bulls eye with center (x, y). This sounds daunting, but I actually found it easier than the original code once I realized how to make a circle with center (x,y) and diameter d. It is
      new Ellipse2D.Double(x - d / 2, y - d / 2, d, d);

      You should now be able to see three bulls eyes. What is the code for the draw method?

    5. Add a translate method that moves the center of a bulls eye. You should be able to Change the paintComponent method of the BullsEyeViewer to
      Graphics2D g2 = (Graphics2D) g;
      BullsEye be1 = new BullsEye(100, 100);
      be1.translate(100, 100);
      be1.translate(-150, 100);

      What is your translate method?

    6. Add a grow method that changes the diameter of the bulls eye by a given amount. For example, if the paintComponent method is changed to
      Graphics2D g2 = (Graphics2D) g;
      BullsEye be1 = new BullsEye(100, 100);
      be1.translate(100, 100);
      be1.translate(-150, 100);

      it should draw this image:

      Hint: You need to add an instance variable for the diameter of the bulls eye. Use that instance variable in the draw method and update it in the grow method. What is the code of your BullsEye class now?