1B1a Programming I 2003

Example Answers for

Programming Notes and Exercises 1


Core questions

Q1.1 Type in the following Java program, compile it and then run it.

Answer: Follow the instructions in the question!


Q1.2 Modify the program in question 1.1 to display the address and phone numbers of the Computer Science Department (hint: look at the web page http://www.cs.ucl.ac.uk). Use a separate output statement for each line of the address. Don't forget that each output statement should end with a semi-colon.

Answer:

// Written by A.N.Expert 10/03
// Program to display the address of the Computer Science department, as
// obtained from http://www.cs.ucl.ac.uk 
class CSAddress
{
  public void displayAddress()
  {
    System.out.println("Department of Computer Science");
    System.out.println("UCL (University College London)");
    System.out.println("Gower Street");
    System.out.println("London");
    System.out.println("WC1E 6BT");
    System.out.println("UK");
    System.out.println("Phone: 020 7679 7214 (+44 20 7679 7214)");
    System.out.println("Fax: 020 7387 1397 (+44 20 7387 1397)");
  }

  public static void main(String[] args)
  {
    CSAddress address = new CSAddress();
    address.displayAddress();
  }
}

What happens if you change println to print in the output statements? Try it and see.

Answer: The entire address and the phone numbers are displayed without any newlines between each part, i.e., all on one line. Print displays a string without  a following newline, while println outputs a string followed by a newline.


Q1.3 Type in, or copy and paste from the web page version of these exercises, the drawing program code presented in the introduction. Save it into a file; remember how the file should be named? Then compile and run the program. Make sure the line displayed is the same as the one in the picture.

Answer: Again, follow the instructions!


Q1.4 Modify your drawing program to draw a hexagon (6 sides). Don't forget to change the name of the drawing program and save it to a new file, otherwise your answer to 1.3 will get overwritten.

Answer:
(Only the statements to draw the hexagon are shown here - the rest of the program is the drawing program template as given on the question sheet.)

public void doDrawing(Graphics g)
{
  //Use trigonometry to workout the coordinates (round them to
  //the nearest integer as g.drawLine only accepts integers...)
  g.drawLine(50, 50, 110, 50);
  g.drawLine(110, 50, 136, 102);
  g.drawLine(136, 102, 110, 154);
  g.drawLine(110, 154, 50, 154);
  g.drawLine(50, 154, 24, 102);
  g.drawLine(24, 102, 50, 50);
}

To do this properly a regular hexagon should be drawn. Each side should be the same length and each interior angle the same. The coordinates of the ends of each line can be easily calculated by exploiting the properties of right angles triangles (double check the coordinates used in this answer - are they correct?).

Coordinates used when drawing are given as integers, while you may calculate coordinates using floating point numbers that then have to be rounded to the nearest integer. Hence, everything is an approximation!

Note that when displayed on the screen, the hexagon may not actually appear to be perfectly regular. This is due to the screen pixels (dots displayed on a screen) not necessarily being square but slightly rectangular. The effect will vary from machine to machine depending on the quality of the monitor and how accurately it is adjusted.


Q1.5 Write a drawing program to draw two rectangles:
One with with top left corner at (30,30) and horizontal sides of length 90 and vertical sides of length 45,
and another with top left corner at (150,40) and horizontal sides of length 60 and vertical sides of length 150.

Draw the first rectangle with drawLine and the second with drawRect.

Answer:

public void doDrawing(Graphics g)
{
  g.drawLine(30,30,120,30);
  g.drawLine(120,30,120,75);
  g.drawLine(120,75,30,75);
  g.drawLine(30,75,30,30);
  g.drawRect(150,40,60,150);
}

The drawRect method was listed on the exercise question sheet.


Q1.6 Write a drawing program to draw an equilateral triangle inside a circle, such that the corners of the triangle touch the circle.

Use drawArc to draw the circle.

Answer: Really just a matter of working out a suitable set of coordinates, using your basic geometry skills. Also you need to have worked out how drawArc works.

As coordinates can only be expressed in integers, you will need to round some of them to the nearest integer but the affect will not be noticeable. If your computer monitor is poorly adjusted the triangle might not appear to be properly equilateral. 

    public void doDrawing(Graphics g)
    {
      //Draws the triangle (length of each side is 100)
      g.drawLine(150, 50, 200, 137);
      g.drawLine(150, 50, 100, 137);
      g.drawLine(100, 137, 200, 137);
      //draw the circle
      g.drawArc(92, 50, 116, 116, 0, 360);
    }

This draws:

 


Q1.7 Write a drawing program to draw a simple picture of the UCL dome. This will need a longer sequence of drawing commands. Don't forget to make use of other drawing shapes such as rectangles and circles. Plan your drawing before trying to write your code.

Answer: plan your picture as a series of lines, rectangles and curves, work out the coordinates needed to draw each shape, then write the lines of code to do that drawing.

Here is an example (your dome should be better!):

    public void paint(Graphics g)
    {  
      g.drawRect(50,200,100,10) ;
      g.drawRect(50,130,100,10) ;
      g.drawRect(94,140,12,60) ;
      g.drawRect(59,140,12,60) ;
      g.drawRect(129,140,12,60) ;
      g.drawLine(50,130,100,115) ;
      g.drawLine(100,115,150,130) ;
      g.drawArc(50,85,100,90,0,180) ;
      g.drawRect(96,65,8,20) ;
    }

Additional questions

Q1.8 Write a drawing program to draw a series of ovals of increasing size. Use drawOval for this.

Answer: This is really about understanding and using the drawOval method. Check the online documentation at: http://www.cs.ucl.ac.uk/teaching/java/javadoc/api/java/awt/Graphics.html for the full details.

    public void doDrawing(Graphics g)
    {
      g.drawOval(50, 50, 45, 60);
      g.drawOval(50, 50, 55, 70);
      g.drawOval(50, 50, 65, 80);
      g.drawOval(50, 50, 75, 90);
      g.drawOval(50, 50, 85, 100);
      g.drawOval(50, 50, 95, 110);
      g.drawOval(50, 50, 105, 120);
      g.drawOval(50, 50, 115, 130);
      g.drawOval(50, 50, 125, 140);
      g.drawOval(50, 50, 135, 150);      
      g.drawOval(50, 50, 145, 160);
    }

This draws:


Q1.9 Write a drawing program to display the CS department address (see the CS web page at http://www.cs.ucl.ac.uk). Each part of the address should appear on a separate line in the normal way.

Use drawString to do this, selecting suitable coordinates to position the lines of text correctly, with consistent spacing between each line.

Answer:

    public void doDrawing(Graphics g)
    {
      g.drawString("Department of Computer Science", 20, 50);
      g.drawString("UCL (University College London)", 20, 70);
      g.drawString("Gower Street", 20, 90);
      g.drawString("London", 20, 110);
      g.drawString("WC1E 6BT", 20, 130);
      g.drawString("UK", 20, 150);
      g.drawString("Phone: 020 7679 7214 (+44 20 7679 7214)", 20, 170);
      g.drawString("Fax: 020 7387 1397 (+44 20 7387 1397)", 20, 190);
    }

This draws:

 


Q1.10 Create a drawing program to draw a bar chart with labels. This will need quite a long list of drawing commands.

Answer:

    public void doDrawing(Graphics g)
    {
      g.drawLine(30, 250, 280, 250);
      g.drawLine(30, 250, 30, 50);
      for (int x = 0; x < 251; x += 50)
      {
        g.drawLine(30 + x, 250, 30 + x, 255);
        g.drawString("" + x, 25 + x, 270);
      }
      for (int y = 0; y < 201; y += 50)
      {
        g.drawLine(30, 250 - y, 25, 250 - y);
        g.drawString("" + y, 3, 255 - y);
      }
      g.drawRect(30, 190, 50, 60);
      g.drawRect(80, 110, 50, 140);
      g.drawRect(130, 130, 50, 120);
      g.drawRect(180, 200, 50, 50);
      g.drawRect(230, 110, 50, 140);
    }

This graph is displayed:


Q1.11 Write a drawing program to draw filled shapes using fillRect, fillArc, fillRoundRect and fill3DRect. Find out about these by looking at the online Java documentation on class Graphics (http://www.cs.ucl.ac.uk/teaching/java/javadoc/api/java/awt/Graphics.html)

Answer: Again an exercise in using the online class documentation.

    public void doDrawing(Graphics g)
    {
      g.fillRect(0, 0, 30, 30);                //draws a black square
      g.setColor(Color.green);                 //sets the colour to green
      g.fillArc(50, 0, 100, 100, 0, 270);      //draws a green arc
      g.setColor(Color.red);                   //sets the colour to red
      g.fillRoundRect(10, 150, 120, 100, 5, 5);//draws a round rectangle
      g.setColor(Color.yellow);                //sets the colour to yellow
      g.fill3DRect(200, 200, 50, 80, true);    //draws a 3D rectangle
      g.setColor(Color.black);                 //restores the colour to black
    }

This displays:


Q1.12 Write a drawing program to draw a sine wave. A method to compute sin is available in the Java class libraries and is called Math.sin. More information is available in the Java documentation.

Answer: This draws a nice looking sine wave. The x coordinate is adjusted to give a reasonable length of wave.

    public void doDrawing(Graphics g)
    {
      int initialHeight = 150; // This is where to start drawing from.
      // This is the (height) zoom factor. Change it to
      // zoom in or out...
      int zoom = 100;
      // Use a loop to draw the sine wave, by calculating the sin
      // for each x value.
      // Note the use of the toRadians method.
      for (int x = 0; x < WIDTH*2; x++)
      {
        int y = (int)(initialHeight - ((Math.sin(Math.toRadians(x)))) * zoom);
        g.drawRect(x/2, y, 1, 1);
      }
    }

Visit http://www.cs.ucl.ac.uk/teaching/java/javadoc/api/java/lang/Math.html for more information about class Math and the various maths functions it provides.

This displays:


Q1.13 Write a drawing program to draw any picture you like using as many features of class Graphics as possible.

Answer: It's really up to your imagination. Here is a simple example. Your answer should be much better!

public void doDrawing(Graphics g)
{
  g.drawRect(1,HEIGHT-101,100,100) ;
  g.drawLine(1,HEIGHT-101,51,HEIGHT-151) ;
  g.drawLine(51,HEIGHT-151,101,HEIGHT-101) ;
  g.drawRect(5,HEIGHT-90,20,20) ;
  g.drawRect(76,HEIGHT-90,20,20) ;
  g.drawRect(41,HEIGHT-51,20,50) ;
  g.fillArc(45,HEIGHT-30,5,7,0,360) ;
  g.setColor(Color.yellow) ;
  g.fillArc(WIDTH-50,0,50,50,0,360) ;
}

Hard Questions

These questions are challenges. Try searching on the web for information - there are lots of interesting websites and examples to be found. Also, discuss these questions with your tutor.

Q1.14 Write a drawing program to draw a fractal shape using a recursive algorithm, like this one:

Note that the shape of the sub-elements reflect the shape of the whole.

Answer:

This is actually known as a Koch snowflake. There is a nice website at http://math.rice.edu/~lanius/frac/koch.html that shows you how to draw it and similar shapes. The code below is a minimal implementation and should be inserted into the drawing program, replacing the existing doDrawing method.

    private int oldX = 70; // Adjust these to determine where the drawing starts.
    private int oldY = 100;

    private void snowflake(Graphics g, int level, float size)
    {
      // The snowflake is just made of three fractal lines
      // drawn round an equilateral triangle.
      line(g, level, size, 0);
      line(g, level, size, 120);
      line(g, level, size, 240);
    }

    private void line(Graphics g, int level, float length, float direction)
    {
      if (level == 0)
      {
        int newX = ((int) (Math.cos(Math.toRadians(direction)) / length)) + oldX;
        int newY = ((int) (Math.sin(Math.toRadians(direction)) / length)) + oldY;
        g.drawLine(oldX, oldY, newX, newY);
        oldX = newX;
        oldY = newY;
      }
      else
      {
        float new_length = length / 3.0F;
        line(g, level - 1, new_length, direction);
        line(g, level - 1, new_length, direction - 60);
        line(g, level - 1, new_length, direction + 60);
        line(g, level - 1, new_length, direction);
      }
    }

    // This part of the program does the actual drawing.
    public void doDrawing(Graphics g)
    {
      snowflake(g, 4, 32.0F); // Modify these arguments to draw different versions of the snowflake.
    }

This snowflake is drawn:


Q1.15 Write a drawing program to display the mandelbrot set in colour. Add as many user interface features as you like. Allow the user to zoom in on interesting areas.

Answer: To find out about Mandelbrot sets, search the web. Here are two example answers. The first is the basic minimum, while the second is more ambitious.

public void doDrawing(Graphics g)
{
  int nColours = 8 ;
  Color colours[] = new Color[nColours];
  colours[0] = Color.black ;
  colours[1] = Color.red ;
  colours[2] = Color.green ;
  colours[3] = Color.blue ;
  colours[4] = Color.cyan ;
  colours[5] = Color.magenta ;
  colours[6] = Color.yellow ;
  colours[7] = Color.white ;
      
  int cols = 300 ;
  int rows = 300 ;
  int iterations = 256 ;
  int size = 4 ;

  for (int col = 0 ; col < cols ; col++) 
  {
    for (int row = 0 ; row < rows ; row++) 
    {
      double x = 0.0 ;
      double y = 0.0 ;
      int colour ;
      for (colour=0 ; colour < iterations ; colour++) 
      {
        if ((x*x + y*y) > size)
        {
          break ;
        }
        double newy = 2*x*y + ((row*0.01) - 1.5) ;
        x = x*x - y*y + ((col*0.01) - 2.0) ;
        y = newy ;
      }
      g.setColor (colours[colour % nColours]) ;
      g.drawLine (col,row,col,row) ;
    }
  }
}

This will display the familiar Mandelbrot shape:

 A more sophisticated version:

/*
 *  This program displays the mandelbrot fractal. It allows the user to
 *  zoom in, reset the fractal and quit.
 *  For an explanation on how this (and other fractals) work, read
 *  http://www.geocities.com/fabioc.
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class BetterMandelbrot extends JFrame
{
  class DrawPanel extends JPanel
     implements MouseMotionListener, MouseListener
  {
    // These set the size of the drawing area.
    // By default this will be 300 by 300 pixels.
    // Change the sizes to suit what you need.
    private int WIDTH = 300;
    private int HEIGHT = 300;
    //coordinate variables
    private final int MAX = 256;
    // max iterations
    private final double SX = -2.025;
    // start (real)
    private final double EX = 0.6;
    // end (real)
    private final double SY = -1.125;
    // start (imaginary)
    private final double EY = 1.125;
    // end (imaginary)
    private int xs, ys, xe, ye;
    private double xstart, ystart, xend, yend, xzoom, yzoom;
    private float xy;
    private boolean action, rectangle, done;
    private Graphics localGraphics;
    private Cursor waitCursor, crosshairCursor;
    // Colour coefficients. Change these to change the colour of
    // the set.
    private float hue = 0.7f;
    private float saturation = 0.7f;
    private float brightness = 1.5f;
    public void mouseExited(MouseEvent event) { }
    public void mouseEntered(MouseEvent event) { }

    public void mouseReleased(MouseEvent event)
    {
      event.consume();
      int z = 0;
      int w = 0;
      if (action)
      {
        xe = event.getX();
        ye = event.getY();
        if (xs > xe)
        {
          z = xs;
          xs = xe;
          xe = z;
        }
        if (ys > ye)
        {
          z = ys;
          ys = ye;
          ye = z;
        }
        w = (xe - xs);
        z = (ye - ys);
        if ((w < 2) && (z < 2))
        {
          calculate();
        }
        else
        {
          if (((float) w > (float) z * xy))
          {
            ye = (int) ((float) ys + (float) w / xy);
          }
          else
          {
            xe = (int) ((float) xs + (float) z * xy);
          }
          xend = xstart + xzoom * (double) xe;
          yend = ystart + yzoom * (double) ye;
          xstart += xzoom * (double) xs;
          ystart += yzoom * (double) ys;
        }
        xzoom = (xend - xstart) / (double) WIDTH;
        yzoom = (yend - ystart) / (double) HEIGHT;
        drawPoints();
        rectangle = false;
        update(this.getGraphics());
      }
    }
    public void mousePressed(MouseEvent event)
    {
      event.consume();
      if (action)
      {
        xs = event.getX();
        ys = event.getY();
      }
    }
    public void mouseClicked(MouseEvent event) { }
    public void mouseMoved(MouseEvent event) { }
    // This controls the zoom.
    public void mouseDragged(MouseEvent event)
    {
      event.consume();
      if (action)
      {
        xe = event.getX();
        ye = event.getY();
        rectangle = true;
        update(this.getGraphics());
      }
    }
    public void init()
    {
      done = false;
      xy = (float) WIDTH / (float) HEIGHT;
      localGraphics = this.getGraphics();
      done = true;
    }
    public void start()
    {
      action = false;
      rectangle = false;
      calculate();
      xzoom = (xend - xstart) / (double) WIDTH;
      yzoom = (yend - ystart) / (double) HEIGHT;
      drawPoints();
    }
    public void update(Graphics g)
    {
      if (rectangle)
      {
        g.setColor(Color.white);
        if (xs < xe)
        {
          if (ys < ye)
          {
            g.drawRect(xs, ys, (xe - xs), (ye - ys));
          }
          else
          {
            g.drawRect(xs, ye, (xe - xs), (ys - ye));
          }
        }
        else
        {
          if (ys < ye)
          {
            g.drawRect(xe, ys, (xs - xe), (ye - ys));
          }
          else
          {
            g.drawRect(xe, ye, (xs - xe), (ys - ye));
          }
        }
      }
    }
    private void drawPoints()
    {
      int x = 0;
      int y = 0;
      float h = 0.0F;
      float b = 0.0F;
      float alt = 0.0f;
      action = false;
      setCursor(waitCursor);
      for (x = 0; x < WIDTH; x += 2)
      {
        for (y = 0; y < HEIGHT; y++)
        {
          // color value
          h = colourPoint(xstart + xzoom * (double) x, ystart + yzoom * (double) y);
          if (h != alt)
          {
            b = 1.0f - h * h;
            localGraphics.setColor(Color.getHSBColor(hue * h, saturation, brightness * b));
            alt = h;
          }
          localGraphics.drawLine(x, y, x + 1, y);
        }
      }
      setCursor(crosshairCursor);
      action = true;
    }
    /*
     *  Defines what colour the given point will be, based on its
     *  coordinates (what it's position is with respect to the center) and the
     *  maximum number of iterations.
     */
    private float colourPoint(double x, double y)
    {
      double r = 0.0;
      double i = 0.0;
      double m = 0.0;
      int j = 0;
      while ((j < MAX) && (m < 4.0))
      {
        j++;
        m = r * r - i * i;
        i = 2.0 * r * i + y;
        r = m + x;
      }
      return (float) j / (float) MAX;
    }
    private void calculate()
    {
      xstart = SX;
      ystart = SY;
      xend = EX;
      yend = EY;
      if ((float) ((xend - xstart) / (yend - ystart)) != xy)
      {
        xstart = xend - (yend - ystart) * (double) xy;
      }
    }
    public DrawPanel()
    {
      addMouseListener(this);
      addMouseMotionListener(this);
      waitCursor = new Cursor(Cursor.WAIT_CURSOR);
      crosshairCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
    }
    // This part of the program makes sure that
    // the window drawing area ends up being the
    // right size. You don't need to change this.
    public Dimension getPreferredSize()
    {
      return new Dimension(WIDTH, HEIGHT);
    }
  }
  private JButton startButton;
  private final DrawPanel drawing;
  
  // Create a new window frame.
  public BetterMandelbrot(String name)
  {
    super(name);
    drawing = new DrawPanel();
    setupWindow();
  }
  private void setupWindow()
  {
    // Create the contents of the window. The top (or Center)
    // part is the drawing area. The bottom (or South) strip
    // holds a quit button.
    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new BorderLayout());
    JButton quitButton = new JButton("Quit");
    JButton resetButton = new JButton("Start");
    buttonPanel.add("Center", quitButton);
    buttonPanel.add("West", resetButton);
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add("Center", drawing);
    getContentPane().add("South", buttonPanel);
    // The event listeners are set up here to enable the
    // program to respond to events.
    quitButton.addActionListener(
      new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          quit();
        }
      });
    resetButton.addActionListener(
      new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          drawing.init();
          drawing.start();
          ((JButton) (event.getSource())).setText("Reset");
        }
      });
    addWindowListener(
      new WindowAdapter()
      {
        public void windowClosing(WindowEvent event)
        {
          quit();
        }
      });
  }
  // This will terminate the program when the user
  // wants to quit.
  private void quit()
  {
    System.exit(0);
  }
  // This part of the program sets everything up
  // and displays the drawing window.
  public static void main(String[] args)
  {
    BetterMandelbrot frame = new BetterMandelbrot("The Mandelbrot Fractal...");
    frame.pack();
    frame.setVisible(true);
  }
}

This will display a window like so: 

 


You can zoom in using the mouse to select an area: