Home
|
Course Outline
|
Lectures
|
Homework
|
Files
The following is a skeleton of the Gaussian.java
program which summarizes the use of API classes and methods:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
These are the three basic API packages which you will want to import
and use in most graphics programs.
public class Gaussian extends Applet
implements ActionListener, ItemListener, Runnable {
The class Gaussian is derived from
java.applet.Applet so it can be run as an applet in a Web browser
as well as an application from the command line. Gaussian also implements
three interfaces:
The following are the fields and methods which implement the Metropolis
algorithm for the Gaussian weight function. Method definitions are
omitted for simplicity.
// define Gaussian probability function
double A = 1;
double sigma = 1;
double xCenter = 0;
boolean twoGaussians;
double A2 = 0.7;
double sigma2 = 0.5;
double xCenter2 = 6;
double p (double x) { }
// Metropolis algorithm
double x0 = -5;
double x = x0;
double delta = 1;
boolean MetropolisStep () { }
long naccept = 0;
long ntotal = 0;
int stepsToDiscard = 0;
void Metropolis () { }
// histogram
int N = 401;
int[] histogram = new int[N];
int hMax;
void initializeHistogram () { }
double xMin = -10;
double xMax = 10;
int pN = N * 5 / 6;
int[] probability = new int[N];
void computeProbability () { }
int xWalker;
int deltaWalker;
void locateWalker () { }
void recordStep () { }
Next, we use a
nested or inner class
to create a screen area or "canvas" on which to draw the histogram and
compare it with the Gaussian probability curve. This inner class
named Picture extends
java.awt.Canvas.
class Picture extends Canvas {
int pixels = N;
Picture () {
setSize(pixels, pixels);
setBackground(new Color(255, 250, 240));
}
public void update (Graphics g) {
paint(g);
}
Image offScreen;
Graphics osg;
Image walker;
The Picture class has a constructor method (which has the same name as
the class). The default update(Graphics g) method of Canvas is called by Java
when it wants to update the canvas. This method erases the canvas when
it is called, and then it calls the paint(Graphics g). Because we don't
want to erase the picture from movie frame to frame, we override
update to simply call paint (which is redefined below) without first
erasing Picture.
We also define two java.awt.Image instances which represent an array of screen pixels in memory which can be copied to the physical screen. The image offScreen is used to implement double buffering, which is necessary to create a smooth animation and avoid "flashing". A GIF file will be loaded into the image walker.
Graphics in the AWT are rendered by instances of java.awt.Graphics, which has a large number of methods you will want to become very familiar with! Note that we also create a Graphics instance named osg to draw on the offScreen image.
Finally, here is the paint(Graphics g) method of Picture which is called by
Java and which we use to do all the required drawing. Note that we are
careful to create offScreen, and to load
walker.gif using the form of getImage()
appropriate for an applet (running in a browser) or an application.
public void paint (Graphics g) {
if (offScreen == null) {
offScreen = createImage(pixels, pixels);
osg = offScreen.getGraphics();
try {
walker = getImage(getDocumentBase(), "walker.gif");
} catch (Exception e) {
walker = Toolkit.getDefaultToolkit().getImage("walker.gif");
}
}
osg.clearRect(0, 0, pixels, pixels);
double scale = Math.max(hMax, N) / (double) pN;
int N0 = N - N / 12;
osg.setColor(Color.red);
if (xWalker >= 0 && xWalker < N) {
int yWalker = N0 - probability[xWalker];
osg.fillOval(xWalker - 4, yWalker - 4, 8, 8);
osg.drawImage(walker, xWalker - 15, yWalker - 20, this);
}
osg.setColor(Color.lightGray);
for (int n = 0; n < N; n++)
osg.drawLine(n, N0, n, N0 - (int) (histogram[n] / scale));
osg.setColor(Color.green);
for (int n = 1; n < N; n++)
osg.drawLine(n - 1, N0 - probability[n - 1],
n, N0 - probability[n]);
osg.setColor(Color.red);
int dy = 16;
osg.fillRect(xWalker - deltaWalker, N0 + dy, 2 * deltaWalker, 8);
int dx = 16;
dy = 12;
int y = N0 + dy;
int x = N / 2;
osg.setColor(Color.blue);
osg.drawLine(x, N0 - 2, x, N0 + 2);
osg.drawString(" 0.0", x - dx, y);
x = N / 4;
osg.drawLine(x, N0 - 2, x, N0 + 2);
osg.drawString("-5.0", x - dx, y);
x = 3 * N / 4;
osg.drawLine(x, N0 - 2, x, N0 + 2);
osg.drawString("+5.0", x - dx, y);
double accept = naccept;
if (ntotal > 0)
accept /= ntotal;
osg.fillRect(10, 8, (int) (80 * accept), 8);
osg.drawRect(10, 8, 80, 8);
osg.drawString("" + (int) (accept * 100) + "% acceptance,"
+ " Total steps = " + ntotal,
100, 16);
g.drawImage(offScreen, 0, 0, null);
}
}
Note how the picture is drawn using the Graphics instance osg of the
offScreen image buffer: first, the buffer is erased using the
clearRect() method; then various drawing commands are issued; and
finally, the offScreen image is drawn to the screen.
Next, we declare an instance of Picture which we have just
defined, four instances of
java.awt.TextField to input parameter values, an instance of
java.awt.Checkbox to select either a single or two Gaussians, and
three instances of
java.awt.Button to control the animation.
Picture picture;
TextField x0TextField;
TextField deltaTextField;
TextField discardTextField;
TextField skipTextField;
int skip;
Checkbox twoGaussiansCheckbox;
Button runButton;
Button thermButton;
Button resetButton;
We next override the init() method of Applet to create the various
"widgets" (i.e., buttons, textFields, etc.) and add them to our
Gaussian class, which is derived from java.applet.Applet. Note that
we add "listeners" to "this" (the picture) to the widgets, so when
the widget is activated by the user it will send an "event" to
picture informing it about the user's action.
public void init () {
initializeHistogram();
computeProbability();
locateWalker();
add(picture = new Picture());
Panel p = new Panel();
p.setLayout(new GridLayout(6, 2));
p.add(new Label("Start point x0 = "));
p.add(x0TextField = new TextField("" + x0));
x0TextField.addActionListener(this);
p.add(new Label("Step size delta = "));
p.add(deltaTextField = new TextField("" + delta));
deltaTextField.addActionListener(this);
p.add(new Label("Steps to discard = "));
p.add(discardTextField = new TextField("" + stepsToDiscard));
discardTextField.addActionListener(this);
p.add(new Label("Skip plot steps = "));
p.add(skipTextField = new TextField("" + skip));
skipTextField.addActionListener(this);
p.add(thermButton = new Button("Junk Therm Steps"));
thermButton.addActionListener(this);
p.add(twoGaussiansCheckbox = new Checkbox("Two peaks"));
twoGaussiansCheckbox.addItemListener(this);
p.add(runButton = new Button("Start"));
runButton.addActionListener(this);
p.add(resetButton = new Button("Reset"));
resetButton.addActionListener(this);
add(p);
}
Thread runThread;
boolean running;
We decare an instance of
java.lang.Thread above: the numerical calculations will run in this
thread in the background so that the applet will remain responsive to user
input.
Because our applet has been registered as a listener with various
widgets, we define an actionPerformed(ActionEvent event) method below
to process the information sent to us by the widgets when the user
interacts with them, for example by typing something in a textField
and hitting the Enter key.
public void actionPerformed (ActionEvent event) {
if (event.getSource() == x0TextField) {
try {
x0 = new Double(event.getActionCommand()).doubleValue();
} catch (NumberFormatException nfe) { }
x0TextField.setText("" + x0);
x = x0;
locateWalker();
} else if (event.getSource() == deltaTextField) {
try {
delta = new Double(event.getActionCommand()).doubleValue();
} catch (NumberFormatException nfe) { }
deltaTextField.setText("" + delta);
} else if (event.getSource() == discardTextField) {
try {
stepsToDiscard = Integer.parseInt(event.getActionCommand());
} catch (NumberFormatException nfe) { }
discardTextField.setText("" + stepsToDiscard);
} else if (event.getSource() == skipTextField) {
try {
skip = Integer.parseInt(event.getActionCommand());
} catch (NumberFormatException nfe) { }
skipTextField.setText("" + skip);
} else if (event.getSource() == thermButton) {
naccept = ntotal = 0;
initializeHistogram();
} else if (event.getSource() == runButton) {
if (running) {
running = false;
runButton.setLabel("Start");
} else {
running = true;
runThread = new Thread(this);
runThread.start();
runButton.setLabel("Stop");
}
} else if (event.getSource() == resetButton) {
if (running) {
running = false;
}
naccept = ntotal = 0;
initializeHistogram();
x = x0;
locateWalker();
picture.repaint();
runButton.setLabel("Start");
}
}
You should study in particular the code executed when the source of
the event is the runButton or the resetButton: in the case of "run",
notice how a new Thread is created and started by calling its
start() method, which in turn calls the run() method (see below).
Next, we define a method to process changes in the one/two Gaussian
checkBox:
public void itemStateChanged (ItemEvent itemEvent) {
if (itemEvent.getSource() == twoGaussiansCheckbox) {
twoGaussians = twoGaussiansCheckbox.getState();
computeProbability();
}
}
Here is the run() method which we are forced to define because our
Applet implements the
java.lang.Runnable interface.
public void run () {
runThread.setPriority(Thread.MIN_PRIORITY);
while (running) {
Metropolis();
recordStep();
picture.repaint();
for (int s = 0; s < skip; s++) {
Metropolis();
recordStep();
}
try {
Thread.sleep(10);
} catch (InterruptedException ie) { }
}
runThread = null;
}
Finally, here is the main() method. This method is invoked by Java when
the Gaussian is run as an application from the command line. An instance
of
java.awt.Frame is created, and the Gaussian instance is added to it.
The statement frame.addWindowListener ... allows the user to
close the frame by clicking on the standard window closing widget. If this
code (which creates an "anonymous class" on the fly) is not added, the
only way to kill the application is to hit Control-C in the console
window.
public static void main (String[] args) {
Gaussian gaussian = new Gaussian();
Frame frame = new Frame("The Gaussian distribution");
frame.addWindowListener(new WindowAdapter () {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.add(gaussian);
gaussian.init();
frame.setSize(700, 450);
frame.setLocation(50, 50);
frame.setVisible(true);
}
}