This applet draws a representation of the famous Mandelbrot
set. It's real purpose, however, is to demonstrate the
use of a separated thread to do long computations. The
thread is not started until the user clicks a "Start" button.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Mandelbrot extends JApplet {
Display canvas; // The drawing surface on which the Mandelbrot
// set is displayed.
JButton stopButton, startButton; // Computation will start when
// Start button is pressed and will
// continue until it finishes or the
// user presses the "Stop" button.
public void init() {
// Initialize the applet by creating the canvas
// and buttons and adding them to the applet's
// content pane.
setBackground(Color.gray);
canvas = new Display();
getContentPane().add(canvas, BorderLayout.CENTER);
JPanel bottom = new JPanel();
bottom.setBackground(Color.gray);
startButton = new JButton("Start");
startButton.addActionListener(canvas);
bottom.add(startButton);
stopButton = new JButton("Stop");
stopButton.addActionListener(canvas);
bottom.add(stopButton);
stopButton.setEnabled(false);
getContentPane().add(bottom, BorderLayout.SOUTH);
} // end init();
public Insets getInsets() {
// Leave space around the applet that will show in the
// background color, gray.
return new Insets(2,2,2,2);
}
public void stop() {
// This method is called by the system when the applet
// is about to be temporarily or permanently stopped.
// To the canvas to stop the computation thread, if
// it is running.
canvas.stopRunning();
}
// --- The following nested class represents the drawing surface
// --- of the applet and also does all the work.
class Display extends JPanel implements ActionListener, Runnable {
Image OSI; // An off-screen images that holds the picture
// of the Mandelbrot set. This is copied onto
// the drawing surface, if it exists. It is
// created by the computational thread.
Graphics OSG; // A graphics context for drawing on OSI.
Thread runner; // A thread to do the computation.
boolean running; // This is set to true when the thread is running.
double xmin = -2.5; // The ranges of x and y coordinates that
double xmax = 1; // are represented by this drawing surface
double ymin = -1.25;
double ymax = 1.25;
public void paintComponent(Graphics g) {
// Called by the system to paint the drawing surface.
// This copies the off-screen image onto the screen,
// if the off-screen image exists. If not, it just
// fills the drawing surface with black.
if (OSI == null) {
g.setColor(Color.black);
g.fillRect(0,0,getWidth(),getHeight());
}
else {
g.drawImage(OSI,0,0,null);
}
}
public void actionPerformed(ActionEvent evt) {
// This will be called when the user clicks on
// the "Start" or "Stop" button. It responds
// by starting or stopping the animation.
String command = evt.getActionCommand();
if (command.equals("Start"))
startRunning();
else if (command.equals("Stop"))
stopRunning();
}
void startRunning() {
// A simple method that starts the computational thread,
// unless it is already running. (This should be
// impossible since this method is only called when
// the user clicks the "Start" button, and that button
// is disabled when the thread is running.)
if (running)
return;
runner = new Thread(this);
// Creates a thread that will execute the run()
// method in this Display class.
running = true;
runner.start();
}
void stopRunning() {
// A simple method that is called to stop the computational
// thread. This is done by setting the value of the
// variable, running. The thread checks this value
// regularly and will terminate when running becomes false.
running = false;
}
int countIterations(double x, double y) {
// The Mandelbrot set is represented by coloring
// each point (x,y) according to the number of
// iterations it takes before the while loop in
// this method ends. For points that are actually
// in the Mandelbrot set, or very close to it, the
// count will reach the maximum value, 80. These
// points will be colored purple. All other colors
// represent points that are definitely NOT in the set.
int count = 0;
double zx = x;
double zy = y;
while (count < 80 && Math.abs(x) < 100 && Math.abs(zy) < 100) {
double new_zx = zx*zx - zy*zy + x;
zy = 2*zx*zy + y;
zx = new_zx;
count++;
}
return count;
}
int i,j; // The center pixel of a square that needs to be
// drawing. These variables are set in the
// run() method of the Display class and are
// used in the run() method of the painter object.
// The same is true for the next two variables.
int size; // The size of the square that needs to be drawn.
int colorIndex; // A number between 1 and 80 that is used
// to decide on the color of the square.
Runnable painter = new Runnable() {
// A Runnable object whose job is to paint a
// square onto the off-screen canvas, and then
// copy that square onto the screen. It will do
// this when its run method is called. The data
// for the square are given by the preceding four
// variables.
public void run() {
int left = i - size/2;
int top = j - size/2;
OSG.setColor( Color.getHSBColor(colorIndex/100.0F,1F,1F) );
OSG.fillRect(left,top,size,size);
paintImmediately(left,top,size,size);
}
};
public void run() {
// This is the run method that is executed by the
// computational thread. It draws the Mandelbrot
// set in a series of passes of increasing resolution.
// In each pass, it fills the applet with squares
// that are colored to represent the Mandelbrot set.
// The size of the squares is cut in half on each pass.
startButton.setEnabled(false); // Disable "Start" button
stopButton.setEnabled(true); // and enable "Stop" button
// while thread is running.
int width = getWidth(); // Current size of this canvas.
int height = getHeight();
OSI = createImage(getWidth(),getHeight());
// Create the off-screen image where the picture will
// be stored, and fill it with black to start.
OSG = OSI.getGraphics();
OSG.setColor(Color.black);
OSG.fillRect(0,0,width,height);
for (size = 64; size >= 1 && running; size = size/2) {
// Outer for loop performs one pass, filling
// the image with squares of the given size.
// The size here is given in terms of pixels.
// Note that all loops end immediately if running
// becomes false.
double dx,dy; // Size of square in real coordinates.
dx = (xmax - xmin)/width * size;
dy = (ymax - ymin)/height * size;
double x = xmin + dx/2; // x-coord of center of square.
for (i = size/2; i < width+size/2 && running; i += size) {
// First nested for loop draws one column of squares.
double y = ymax - dy/2; // y-coord of center of square
for (j = size/2; j < height+size/2 && running; j += size) {
// Innermost for loop draws one square, by
// counting iterations to determine what
// color it should be, and then invoking the
// "painter" object to actually draw the square.
colorIndex = countIterations(x,y);
try {
SwingUtilities.invokeAndWait(painter);
}
catch (Exception e) {
}
y -= dy;
}
x += dx;
Thread.yield(); // Give other threads a chance to run.
}
}
running = false; // The thread is about to end, either
// because the computation is finished
// or because running has been set to
// false elsewhere. In the former case,
// we have to set running = false here
// to indicate that the thread is no
// longer running.
startButton.setEnabled(true); // Reset states of buttons.
stopButton.setEnabled(false);
} // end run()
} // end nested class Display
} // end class Mandelbrot
No comments:
Post a Comment