// FernApplet
// by Doug Babcock
// June - July 2004
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.Random;
/**
* A <code>Fern</code> object creates a window for drawing the Barnsley fern.
* The window contains: a drop-down box to select the color scheme to use when
* drawing the fern; a button to start and stop drawing of the fern; and an
* area to actually draw the fern in.
*
* @author Doug Babcock
*/
public class FernApplet extends JApplet implements ActionListener {
/** The text to appear on the button when drawing is not in progress. */
private static String BUTTON_TEXT_OFF = "Draw Fern";
/** The text to appear on the button when drawing is in progress. */
private static String BUTTON_TEXT_ON = "Stop";
/** Which color scheme to use (set from the comboBox) */
private static int colorScheme = 0;
/** A reference to the JButton that draws (and stops drawing) the fern. */
JButton drawButton = null;
/** A reference to the JComboBox that allows the user to select a color scheme. */
JComboBox colorChoices = null;
/** A reference to the FernArea, the area in which the fern is drawn. */
private FernArea myFernArea = null;
/** A reference to a FernDraw, a Thread which draws the fern. */
private FernDraw myFernDraw = null;
public void init() {
JFrame.setDefaultLookAndFeelDecorated(true);
JPanel mainPane = new JPanel();
mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
// Make the combo box and have the Fern listen to it.
String[] colors = {"Green", "Gradient", "Crystal", "Psycho"};
colorChoices = new JComboBox(colors);
colorChoices.setSelectedIndex(0);
colorChoices.addActionListener(this);
// Set the combo box on a JPanel, give it a label, and set its max size.
JPanel comboBoxPanel = new JPanel();
comboBoxPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
comboBoxPanel.add(new JLabel("Color Scheme:"));
comboBoxPanel.add(colorChoices);
comboBoxPanel.setMaximumSize(comboBoxPanel.getPreferredSize());
// Make the button.
drawButton = new JButton(BUTTON_TEXT_OFF);
drawButton.addActionListener(this);
// Set the button on a JPanel and set its max size.
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
buttonPanel.add(drawButton);
buttonPanel.setMaximumSize(buttonPanel.getPreferredSize());
JPanel topPanel = new JPanel();
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
topPanel.add(comboBoxPanel);
topPanel.add(buttonPanel);
// Make the FernArea.
myFernArea = new FernArea();
// Add everything to the specified container.
//mainPane.add(comboBoxPanel);
//mainPane.add(buttonPanel);
mainPane.add(topPanel);
mainPane.add(myFernArea);
mainPane.setOpaque(true);
setContentPane(mainPane);
}
public void start() {
}
public void stop() {
}
public void destroy() {
}
public void actionPerformed(ActionEvent event) {
if ("comboBoxChanged".equals(event.getActionCommand())) {
// The user selected a color scheme, so let's store the value.
colorScheme = colorChoices.getSelectedIndex();
}
else if (BUTTON_TEXT_OFF.equals(event.getActionCommand())) {
// The user wants us to start drawing.
// Change the button text so that its new function is apparent.
drawButton.setText(BUTTON_TEXT_ON);
// Start a myFernDraw thread, using the size of the FernArea.
myFernDraw = new FernDraw(myFernArea.getWidth(),
myFernArea.getHeight());
myFernDraw.setPriority(Thread.NORM_PRIORITY - 1);
myFernDraw.setDaemon(true);
myFernDraw.start();
}
else if (BUTTON_TEXT_ON.equals(event.getActionCommand())) {
// The user wants us to stop drawing.
// Change the button text so that its new function is apparent.
drawButton.setText(BUTTON_TEXT_OFF);
// Tell the myFernDraw thread to stop drawing.
myFernDraw.interrupt();
}
}
/**
* The <code>FernArea</code> object is a JComponent in which to draw the
* Barnsley fern.
*/
private class FernArea extends JComponent {
/** The preferred (default) size of the <code>FernArea</code>. */
private Dimension preferredSize = new Dimension(600, 400);
/** The smallest possible size of the <code>FernArea</code>. */
private Dimension minimumSize = new Dimension(100, 200);
/**
* Sets the background color of the <code>FernArea</code>, and makes
* it opaque.
*/
public FernArea() {
setBackground(Color.BLACK);
setOpaque(true);
}
/** Returns the minimum size of the <code>FernArea</code>. */
public Dimension getMinimumSize() {
return minimumSize;
}
/** Returns the preferred (default) size of the <code>FernArea</code>. */
public Dimension getPreferredSize() {
return preferredSize;
}
/**
* Paints the contents of the <code>FernArea</code> by copying the
* offscreen image from the <code>FernDraw</code> thread (if available).
* Otherwise just fills in the area with the background color.
*/
protected void paintComponent(Graphics g) {
if (myFernDraw != null) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(myFernDraw.getImage(), 0, 0, null);
}
else {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}
/**
* The <code>FernDraw</code> class is a Thread that draws the fern in the
* <code>FernArea</code>.
*/
private class FernDraw extends Thread {
/** The x-coordinate of the current point. */
private double xval = 0.0;
/** The y-coordinate of the current point. */
private double yval = 0.0;
/** The offscreen image to paint to. */
private BufferedImage offScrn = null;
/** The <code>Graphics</code> object to use for the offscreen image. */
private Graphics big = null;
/** Random number generator for playing the "Chaos Game". */
private Random rand = new Random();
/**
* A counter of the number of times since the last time the fourth
* matrix transformation was used. Useful for certain color schemes.
*/
int misc;
/**
* Creates the offscreen image and its <code>Graphics</code> object,
* and draws the background. */
public FernDraw(int width, int height) {
offScrn = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
big = offScrn.createGraphics();
big.setColor(Color.BLACK);
big.fillRect(0, 0, width, height);
}
/**
* Selects a new point using one of the matrix transformations, draws
* it to the offscreen image, and tells the <code>FernArea</code> to
* repaint where that point is.
*/
protected void drawOnePoint() {
int p = rand.nextInt(100);
double newx;
double newy;
// Compute where the new point should be
if (p < 85) {
newx = xval * 0.85 + yval * 0.04 + 0.00;
newy = xval * -0.04 + yval * 0.85 + 1.60;
xval = newx;
yval = newy;
++misc;
} else if (p < 92) {
newx = xval * 0.20 + yval * -0.26 + 0.00;
newy = xval * 0.23 + yval * 0.22 + 1.60;
xval = newx;
yval = newy;
++misc;
} else if (p < 99) {
newx = xval * -0.15 + yval * 0.28 + 0.00;
newy = xval * 0.26 + yval * 0.24 + 0.44;
xval = newx;
yval = newy;
++misc;
} else {
newx = xval * 0.00 + yval * 0.00 + 0.00;
newy = xval * 0.00 + yval * 0.16 + 0.00;
xval = newx;
yval = newy;
misc = 0;
}
// Set the color to draw based on the current color scheme
switch (colorScheme) {
case 0: big.setColor(Color.GREEN); break;
case 1: big.setColor(new Color((int)Math.abs(xval * 70), 255 -
(int)Math.abs(xval * 70), 0)); break;
case 2: big.setColor(new Color(rand.nextInt(256),
rand.nextInt(256), rand.nextInt(256))); break;
case 3: big.setColor(new Color(Math.max(255 - (misc * 1), 0),
Math.min(misc * 1, 255), 128 + rand.nextInt(127))); break;
}
// Draw the new point
big.fillRect((int)((xval + 5) * offScrn.getWidth() / 10),
(int)(offScrn.getHeight() - (yval * offScrn.getHeight() / 10))
, 1, 1);
// Let the FernArea know that this point was drawn
myFernArea.repaint(new Rectangle(
(int)((xval + 5) * offScrn.getWidth() / 10),
(int)(offScrn.getHeight() - (yval * offScrn.getHeight() / 10))
, 1, 1));
}
/** Returns the offscreen image. */
public BufferedImage getImage() {
return offScrn;
}
/** Runs the drawing process. */
public void run() {
myFernArea.repaint();
misc = 0;
while (!isInterrupted()) {
drawOnePoint();
}
}
}
}
|