Appendix B  Java 2D graphics

The Java library includes a simple package for drawing 2D graphics, called java.awt. AWT stands for “Abstract Window Toolkit”. We are only going to scratch the surface of graphics programming; you can read more about it in the Java tutorials at https://docs.oracle.com/javase/tutorial/2d/.

B.1  Creating graphics

There are several ways to create graphics in Java; the simplest way is to use java.awt.Canvas and java.awt.Graphics. A Canvas is a blank rectangular area of the screen onto which the application can draw. The Graphics class provides basic drawing methods such as drawLine, drawRect, and drawString.

Here is an example program that draws a circle using the fillOval method:

import java.awt.Canvas; import java.awt.Graphics; import javax.swing.JFrame; public class Drawing extends Canvas {
public static void main(String[] args) { JFrame frame = new JFrame("My Drawing"); Canvas canvas = new Drawing(); canvas.setSize(400, 400); frame.add(canvas); frame.pack(); frame.setVisible(true); } public void paint(Graphics g) { g.fillOval(100, 100, 200, 200); } }

The Drawing class extends Canvas, so it has all the methods provided by Canvas, including setSize. You can read about the other methods in the documentation, which you can find by doing a web search for “Java Canvas”.

In the main method, we:

  1. Create a JFrame object, which is the window that will contain the canvas.
  2. Create a Drawing object (which is the canvas), set its width and height, and add it to the frame.
  3. Pack the frame (resize it) to fit the canvas, and display it on the screen.

Once the frame is visible, the paint method is called whenever the canvas needs to be drawn; for example, when the window is moved or resized. The application doesn’t end after the main method returns; instead, it waits for the JFrame to close. If you run this code, you should see a black circle on a gray background.

B.2  Graphics methods

You are probably used to Cartesian coordinates, where x and y values can be positive or negative. In contrast, Java uses a coordinate system where the origin is in the upper-left corner. That way, x and y are always positive integers. Figure B.1 shows these coordinate systems.

Graphical coordinates are measured in pixels; each pixel corresponds to a dot on the screen.


Figure B.1: Diagram of the difference between Cartesian coordinates and Java graphical coordinates.

To draw on the canvas, you invoke methods on a Graphics object. You don’t have to create the Graphics object; it gets created when you create the Canvas, and it gets passed as an argument to paint.

The previous example used fillOval, which has the following signature:

/** * Fills an oval bounded by the specified rectangle with * the current color. */ public void fillOval(int x, int y, int width, int height)

The four parameters specify a bounding box, which is the rectangle in which the oval is drawn. x and y specify the the location of the upper-left corner of the bounding box. The bounding box itself is not drawn (see Figure B.2).


Figure B.2: Diagram of an oval inside its bounding box.

To choose the color of a shape, invoke setColor on the Graphics object:

g.setColor(Color.red);

The setColor method determines the color of everything that gets drawn afterward. Color.red is a constant provided by the Color class; to use it you have to import java.awt.Color. Other colors include:

black blue cyan darkGray gray green lightGray magenta orange pink white yellow

You can create your own colors by specifying the red, green, and blue (RGB) components. For example:

Color purple = new Color(128, 0, 128);

Each value is an integer in the range 0 (darkest) to 255 (lightest). The color (0, 0, 0) is black, and (255, 255, 255) is white.

You can set the background color of the Canvas by invoking setBackground:

canvas.setBackground(Color.white);

B.3  Example drawing

Suppose we want to draw a “Hidden Mickey”, which is an icon that represents Mickey Mouse (see https://en.wikipedia.org/wiki/Hidden_Mickey). We can use the oval we just drew as the face, and then add two ears. To make the code more readable, let’s use Rectangle objects to represent bounding boxes.

Here’s a method that takes a Rectangle and invokes fillOval:

public void boxOval(Graphics g, Rectangle bb) { g.fillOval(bb.x, bb.y, bb.width, bb.height); }

And here’s a method that draws Mickey Mouse:

public void mickey(Graphics g, Rectangle bb) { boxOval(g, bb); int dx = bb.width / 2; int dy = bb.height / 2; Rectangle half = new Rectangle(bb.x, bb.y, dx, dy); half.translate(-dx / 2, -dy / 2); boxOval(g, half); half.translate(dx * 2, 0); boxOval(g, half); }

The first line draws the face. The next three lines create a smaller rectangle for the ears. We translate the rectangle up and left for the first ear, then to the right for the second ear. The result is shown in Figure B.3.


Figure B.3: A “Hidden Mickey” drawn using Java graphics.

You can read more about Rectangle and translate in Chapter 10. See the exercises at the end of this appendix for more example drawings.

B.4  Vocabulary

AWT:
The “Abstract Window Toolkit”, a Java package for creating graphical user interfaces.
coordinate:
A value that specifies a location in a two-dimensional graphical window.
pixel:
The unit in which coordinates are measured.
bounding box:
A common way to specify the coordinates of a rectangular area.
RGB:
A color model based on adding red, green, and blue light.

B.5  Exercises

The code for this chapter is in the ap02 directory of ThinkJavaCode. See page ?? for instructions on how to download the repository. Before you start the exercises, we recommend that you compile and run the examples.

Exercise 1   Draw the flag of Japan: a red circle on a white background that is wider than it is tall.
Exercise 2   Modify Mickey.java to draw ears on the ears, and ears on those ears, and more ears all the way down until the smallest ears are only 3 pixels wide.

The result should look like “Mickey Moose”, shown in Figure B.4. Hint: You should only have to add or modify a few lines of code.


Figure B.4: A recursive shape we call “Mickey Moose”.

Exercise 3   In this exercise, you will draw “Moiré patterns” that seem to shift around as you move. For an explanation of what is going on, see https://en.wikipedia.org/wiki/Moire_pattern.
  1. In the directory app02 in the repository for this book, you’ll find a file named Moire.java. Open it and read the paint method. Draw a sketch of what you expect it to do. Now run it. Did you get what you expected?
  2. Modify the program so that the space between the circles is larger or smaller. See what happens to the image.
  3. Modify the program so that the circles are drawn in the center of the screen and concentric, as in Figure B.5 (left). The distance between the circles should be small enough that the Moiré interference is apparent.

    Figure B.5: Graphical patterns that can exhibit Moiré interference.

  4. Write a method named radial that draws a radial set of line segments as shown in Figure B.5 (right), but they should be close enough together to create a Moiré pattern.
  5. Just about any kind of graphical pattern can generate Moiré-like interference patterns. Play around and see what you can create.
Text © Allen Downey and Chris Mayfield. Interactive HTML © Trinket. Both provided under a CC-NC-BY license. Think Java 1st Edition, Version 6.1.3. 2nd Edition available here.