Drawing Rectangles
Next we’ll write an applet that fills the screen with lots of randomly sized and positioned rectangles in the style of Piet Mondrian. In the process we’ll learn the basics of applet graphics. We’re going to take this one step at a time, adding a bit as we go.
In the first applet we’ll just draw a rectangle on the screen. We’ll get the size of the applet as specified in the HTML file, and then we’ll draw a rectangle around the applet to frame it. Here’s the code:
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian1 extends Applet {
int height, width;
public void init() {
Dimension d = size();
height = d.height;
width = d.width;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, width, height);
}
}
Compile this applet; move the resulting class file to your classes directory, and create an HTML file that points to it. Set the height of the applet to 300 pixels and the width to 300 pixels as well. Load that file into your browser and what do you see? Probably not what you expected. You should see half a rectangle. What happened to the other half?
This is called a fencepost error. The applet lives in a square 300 pixels tall by 300 pixels wide. However the upper left hand corner of the applet starts at (0, 0), not at (1, 1). This means that the applet includes the points with x and y coordinates between 0 and 299, not between 0 and 300. We drew a rectangle 301 pixels high and 301 pixels wide so the edges were chopped off.
This is fortuitous however. Not only does it allow me the opportunity to digress on fencepost errors (which, although annoying, are far less dangerous in Java than in C since Java does check array boundaries) but it also shows us something else. In Java the coordinate system for an applet begins in the upper left hand corner and increases to the right and down. This is common in computer graphics but is different from the Cartesian coordinate system where the direction of increasing y is generally assumed to be up.
Correcting the fence post error is easy. We just change g.drawRect(0, 0, width, height); to g.drawRect(0, 0, width-1, height-1);
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian2 extends Applet {
int height, width;
public void init() {
Dimension d = size();
height = d.height;
width = d.width;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, width-1, height-1);
}
}
As usual compile this and load it into your browser. If the problem isn’t fixed check to make sure that you moved the new class file into the classes directory and that you modified the HTML file to point to Mondrian2.
We’ve introduced exactly one new statement in all this code, drawRect which is a method in the Graphics class. The line g.drawRect(0, 0, height-1, width-1) instructs the Graphics class g to draw a rectangle beginning at the point (0, 0) and ending at the point (299, 299).
This particular rectangles encompasses the entire applet’s visible space. There is nothing to keep us from drawing outside the applet, in fact we did exactly that in our first version where we actually extended the rectangle to (300, 300); but anything we draw there won’t be seen by the user.
The drawRect method draws an open rectangle. If we want to draw a filled rectangle we use the fillRect method. Otherwise the syntax is identical. In Mondrian3 we’ll draw a filled rectangle in the center of the applet. Here’s the code:
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian3 extends Applet {
int AppletHeight;
int AppletWidth;
int RectHeight;
int RectWidth;
int RectTop;
int RectLeft;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectHeight = AppletHeight/3;
RectWidth = AppletWidth/3;
RectTop = (AppletHeight – RectHeight)/2;
RectLeft= (AppletWidth – RectWidth)/2;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
This latest example also demonstrates one other thing. Until now you may have thought that we were passing two points to the drawRect and fillRect methods and drawing the rectangle that joins them. This is how rectangles are implemented in QuickDraw on the Mac for example. However if that was the case the preceding rectangle would have been drawn between (100, 100) and (100, 100), a fairly small rectangle. Since that isn’t the case our association of the last two variables with width and height must be correct.
The extremely astute reader may object at this point. Until now we’ve only drawn squares. Although the last two variables passed to drawRect and fillRect must be the height and the width how do we know which is which? The simplest way to tell is to write a test program that draws a non-square rectangle. Let’s try that now:
//Draw a rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian4 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectHeight = AppletHeight/3;
RectWidth = (AppletWidth*2)/3;
RectTop = (AppletHeight – RectHeight)/2;
RectLeft= (AppletWidth – RectWidth)/2;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
So you see that the third argument is indeed the width and the fourth is the height.
Now that we’ve learned how to draw rectangles, both filled and unfilled, let’s make life a little more exciting by randomly selecting the position and size of the rectangle. To do this we’ll need the Math.random() method from java.lang.Math. This method returns a double between 0.0 and 1.0 so we’ll need to multiply the result by the applet’s height and width to get a reasonably sized rectangle that fits into our applet space. To do this we’ll create the following Randomize method:
private int Randomize( int range )
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
This method forces the result of Math.random into an int in the range we require. Pay special attention to the last line. When you see a raw type in parentheses like (int) or (float) it’s a cast. Casts change one value type into another. Thus here we’re changing a double into an int. The cast rounds as necessary.
Casting in Java is safer than in C or other languages that allow arbitrary casting. Java only lets casts occur when they make sense, such as a cast between a float and an int. However you can’t cast between an int and a String for example.
//Draw a rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian5 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight – RectTop);
RectWidth = Randomize(AppletWidth – RectLeft);
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
Occasionally this applet does randomly produce a rectangle that’s two small to see so if you don’t see anything, reload it. Reload it a few times. Each time you’ll see a rectangle of a different size appear in a different place.
Let’s make our world a little more colorful. To do this we’ll change the rectangle color to red. To do this we’ll use a new methods setColor(), part of the Graphics class.
//Draw a colored rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian6 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight – RectTop);
RectWidth = Randomize(AppletWidth – RectLeft);
repaint();
}
public void paint(Graphics g) {
// g.setBackground(Color.white);
g.setColor(Color.red);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
The awt predefines a number of colors including:
* black
* blue
* cyan
* darkGray
* gray
* green
* lightGray
* magenta
* orange
* pink
* red
* white
* yellow
If these aren’t sufficient for your needs, you can define others using the same RGB triple that’s used to set background colors on many web pages. You even get to use decimal numbers instead of the hex values you have to use for the bgcolor tag. For example to select a medium gray you’d use Color(127, 127, 127). Pure white would be Color(255, 255, 255). Pure red is (255, 0, 0) and so on.
By using the color constructor we can expand our program to select not only a random rectangle but also a random color for the rectangle. Here’s the code:
//Draw a randomly colored rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian7 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight – RectTop);
RectWidth = Randomize(AppletWidth – RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
repaint();
}
public void paint(Graphics g) {
// g.setBackground(Color.white);
g.setColor(RectColor);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
In the next example we’re going to draw multiple randomly sized, randomly colored rectangles. Since we want each rectangle to be different we’re going to have to move the calculation of the rectangle’s shape, position and color into the paint() method. Here’s the code:
//Draw many randomly colored rectangles
import java.applet.Applet;
import java.awt.*;
public class Mondrian8 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
int numberRectangles = 100;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
for (int i=0; i < numberRectangles; i++) {
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight – RectTop);
RectWidth = Randomize(AppletWidth – RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
g.setColor(RectColor);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
Finally let’s let the HTML specify the number of rectangles to be drawn in one pass. We’ll keep the default value as is and only replace it if the HTML includes a Number PARAM.
//Draw many random rectangles
import java.applet.Applet;
import java.awt.*;
public class Mondrian9 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
int numberRectangles = 100;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
String s = getParameter(”Number”);
if (s != null) {
numberRectangles = Integer.valueOf(s).intValue();
}
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
for (int i=0; i < numberRectangles; i++) {
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight – RectTop);
RectWidth = Randomize(AppletWidth – RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
g.setColor(RectColor);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
That’s all for now, but we’ll return to Mondrian at the end of this chapter when we add threading, and show how to draw rectangles continuously.
Exercises
1. For the artistically inclined: write a version of Mondrian that draws pictures that are more believably in the style of Piet Mondrian. You should probably restrict your color choices and not allow rectangles to overlap.