// Mandelbrot set incorporating Mouse listener.
// Added display of appropriate Julia set on right-click
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.image.*;
import java.text.*;
class Complex {
  // implement complex arithmetic only in so far
  // as is needed to compute Z = Z*Z + C
  private double r;
  private double i;
  public Complex(double a, double b) {r = a; i = b;}
  public void set(double a, double b) {r = a; i = b;}
  public double getMagnitudeSquared() {return r*r + i*i; }
  Complex setNextValue(Complex C) {
    double rp = r, ip = i;
    r = r*r - i*i + C.r;
    i = 2*rp*ip + C.i;
    return this;
  }
  public String toString() {return "(" + r + ", " + i + ")";}
}
class Boundary {
// package xLeft, xRight, yCenter
  double xLeft;
  double xRight;
  double yCenter;
  double xJulia, yJulia;
  boolean julia;
  Boundary(double l, double r, double c, boolean j, double xj, double yj) {
    xLeft = l; xRight = r; yCenter = c;
    julia = j; xJulia = xj; yJulia = yj;
  }
  boolean equals(Boundary x) {
    return xLeft == x.xLeft
        && xRight == x.xRight
        && yCenter == x.yCenter
        && julia == x.julia
       && (julia
            ? xJulia == x.xJulia && yJulia == x.yJulia
            : true);
  }
  public String toString() {
    return xLeft + " " + xRight + " " + yCenter
      + " " + julia + " " + xJulia + " " + yJulia; }
}
class PointArray {
  // holds the calculated points
  static void setUp(int sizeIn) {
    size = sizeIn;
    // set up sizeXsize static array of points
    arrayPoints = new int[size][size];
  }
  static void computePoints(boolean pushSwitch) {
    if (pushSwitch || stk.empty()) {
      Boundary b = new Boundary(xLeft, xRight, yCenter,
                   julia, xJulia, yJulia);
      // Java5: if (stk.empty() || !(b.equals(stk.peek())))
      if (stk.empty() || !(b.equals((Boundary)stk.peek())))
        stk.push(b);
    }
    double a, b;
    double lowb = yCenter - (xRight - xLeft)/2;
    double delta = (xRight - xLeft) / size;
    int k;
    Complex z = new Complex(0,0);
    maxVal = 0; minVal = loopCount;
    a = xLeft;
    if (!julia) {
      for (int i=0; i<size; i++) {
        b = lowb;
        for (int j=0; j<size; j++) {
          z.set(0,0);
          C.set(a,b);
          for (k=0; k<loopCount; k++)
            if (z.setNextValue(C).getMagnitudeSquared() >= 4) break;
          arrayPoints[i][j] = k;
          if (k < loopCount) {
            if (k > maxVal) maxVal = k;
            if (k > 0)
            if (k < minVal) minVal = k;
          }
          b = b + delta;
        }
        a = a + delta;
      }
    }
    else {
      C.set(xJulia,yJulia);
      for (int i=0; i<size; i++) {
        b = lowb;
        for (int j=0; j<size; j++) {
          z.set(a,b);
          for (k=0; k<loopCount; k++)
            if (z.setNextValue(C).getMagnitudeSquared() >= 4) break;
          arrayPoints[i][j] = k;
          if (k < loopCount) {
            if (k > maxVal) maxVal = k;
            if (k > 0)
            if (k < minVal) minVal = k;
          }
          b = b + delta;
        }
        a = a + delta;
      }
    }
    // set up image from computed points
    if (colorArray == null) {
      colorArray = new int[12];
      // Colors, outside to close in
      colorArray[0] = 0xFFF8F8F8; //gray
      colorArray[1] = 0xFFFF0000; //red
      colorArray[2] = 0xFFFFCC00; //orange
      colorArray[3] = 0xFFFFFF00; //yellow
      colorArray[4] = 0xFFCCFF00; //yellow-green
      colorArray[5] = 0xFF00FF00; //green
      colorArray[6] = 0xFF00FFCC; //blue-green
      colorArray[6] = 0xFF00FFFF; //cyan
      colorArray[7] = 0xFF00CCFF; //light blue
      colorArray[8] = 0xFF0000FF; //blue
      colorArray[9] = 0xFFCC00FF; //blue-violet
      colorArray[10] = 0xFFFF00FF; //magenta
      colorArray[11] = 0xFFFF00CC; //red-violet
    }
    int testVal;
    int partition = (maxVal-minVal)/12+1;
    image = new BufferedImage(size,size,BufferedImage.TYPE_INT_ARGB);
    for (int i=0; i<PointArray.size; i++)
      for (int j=0; j<PointArray.size; j++) {
        testVal = arrayPoints[i][j];
        if (testVal == loopCount) image.setRGB(i,size-j-1,0xFF000000);
        else if (testVal == 0) image.setRGB(i,size-j-1,0xFFFFFFFF);
        else
          image.setRGB(i,size-j-1,colorArray[(int)((testVal-minVal)/partition)]);
    }
  }
  static int[][] arrayPoints;
  static int[] colorArray;
  static int maxVal;
  static int minVal;
  static int size;
  static BufferedImage image;
  static int loopCount = 512;
  static double xLeft, xRight, yCenter;
  static double xJulia, yJulia;
  static boolean julia = false;
  static Complex C = new Complex(0.0,0.0);
  // Java5: static Stack<Boundary> stk = new Stack<Boundary>();
  static Stack stk = new Stack();
}

class ButtonHandler extends JPanel implements ActionListener {
// produce and respond to buttons
  ButtonHandler(TextHandler textP, MyCanvas graphicsP) { // constructor sets up vertical panel of buttons
    super();
    textPanel = textP; graphicsPanel = graphicsP;
    setLayout(new GridLayout(0,1)); // unlimited rows, 1 column
    // add buttons
    add(runButton);
    add(homeButton);
    add(backButton);
    add(zoom2Button);
    add(out2Button);
    add(zoom10Button);
    add(out10Button);
    add(blkX2Button);
    add(blkO2Button);
    loopText.setText("512");
    add(loopText);
    // register button listeners
    runButton.addActionListener(this);
    homeButton.addActionListener(this);
    zoom2Button.addActionListener(this);
    zoom10Button.addActionListener(this);
    out2Button.addActionListener(this);
    out10Button.addActionListener(this);
    backButton.addActionListener(this);
    blkX2Button.addActionListener(this);
    blkO2Button.addActionListener(this);
  }
  double getDouble(String text, double old) {
    double val;
    try {
      val = Double.parseDouble(text);
    }
    catch (NumberFormatException nf) {
      val = old;
    }
    return val;
  }
  int getLoopCount() {
    int val;
    try {
      val = Integer.parseInt(loopText.getText());
    }
    catch (NumberFormatException nf) {
      val = 512;
      loopText.setText("512");
    }
    return val;
  }
  //  buttons
  private JButton runButton = new JButton("Run");
  private JButton homeButton = new JButton("Home");
  private JButton backButton = new JButton("Back");
  private JButton zoom2Button = new JButton("X2");
  private JButton zoom10Button = new JButton("X10");
  private JButton out2Button = new JButton("/2");
  private JButton out10Button = new JButton("/10");
  private JButton blkX2Button = new JButton("BlkX2");
  private JButton blkO2Button = new JButton("Blk/2");
          JTextField loopText = new JTextField(7);
  // the Listener
  // comes here when one of the above buttons is clicked
  public void actionPerformed(ActionEvent e) {
  double xLeft, xRight, yCenter;
  boolean pushSwitch = true;
  xLeft = PointArray.xLeft;
  xRight = PointArray.xRight;
  yCenter = PointArray.yCenter;
    String action = e.getActionCommand();
    if (action.equals("Run")) {
      // get contents of responses
      xLeft = getDouble(textPanel.xLeftText.getText(), xLeft);
      xRight = getDouble(textPanel.xRightText.getText(), xRight);
      yCenter = getDouble(textPanel.yCenterText.getText(), yCenter);
    }
    else if (action.equals("Home")) {
      xLeft = textPanel.ORIGINAL_XLEFT;
      xRight = textPanel.ORIGINAL_XRIGHT;
      yCenter = textPanel.ORIGINAL_YCENTER;
      PointArray.loopCount = 512;
      PointArray.julia = false;
      loopText.setText(Integer.toString(PointArray.loopCount));
      PointArray.stk.clear();
    }
    else if (action.equals("X2")) {
      double quarter = (xRight - xLeft) / 4;
      xLeft = xLeft + quarter;
      xRight = xRight - quarter;
    }
    else if (action.equals("/2")) {
      double adjust = (xRight - xLeft) / 2;
      xLeft = xLeft - adjust;
      xRight = xRight + adjust;
    }
    else if (action.equals("X10")) {
      double adjust = 9 * (xRight - xLeft) / 20;
      xLeft = xLeft + adjust;
      xRight = xRight - adjust;
    }
    else if (action.equals("/10")) {
      double adjust = 9 * (xRight - xLeft) / 2;
      xLeft = xLeft - adjust;
      xRight = xRight + adjust;
    }
    else if (action.equals("Back")) {
      if (!(PointArray.stk.empty())) {
        // Java5: Boundary b = PointArray.stk.pop();
        Boundary bc = new Boundary(xLeft,xRight,yCenter,
                                   PointArray.julia,
                                   PointArray.xJulia,
                                   PointArray.yJulia);
        Boundary b = (Boundary)PointArray.stk.pop();
        if (b.equals(bc) && !(PointArray.stk.empty()))
          // current state was TOS get another if available
          b = (Boundary)PointArray.stk.pop();
        xLeft = b.xLeft;
        xRight = b.xRight;
        yCenter = b.yCenter;
        PointArray.julia = b.julia;
        PointArray.xJulia = b.xJulia;
        PointArray.yJulia = b.yJulia;
      }
    }
    else if (action.equals("BlkX2")) {
      PointArray.loopCount *= 2;
      loopText.setText(Integer.toString(PointArray.loopCount));
      pushSwitch = false; // just changing loopCount, don't push
    }
    else if (action.equals("Blk/2")) {
      PointArray.loopCount /= 2;
      loopText.setText(Integer.toString(PointArray.loopCount));
      pushSwitch = false; // just changing loopCount, don't push
    }
    else return; // do nothing (should never get here)
    // set wait cursor for graphicsPanel and buttonPanel
    graphicsPanel.setCursor(graphicsPanel.waitCursor);
    setCursor(graphicsPanel.waitCursor);
    PointArray.xLeft = xLeft;
    PointArray.xRight = xRight;
    PointArray.yCenter = yCenter;
    textPanel.updatePanel();
    PointArray.computePoints(pushSwitch);
    graphicsPanel.repaint();
  }
  // other panel locations (set in constructor)
  TextHandler textPanel;
  MyCanvas graphicsPanel;
}

class TextHandler extends JPanel {
// Set Up label and text fields,
  TextHandler() {
    super();
    // set up text fields in a grid
    setLayout(new GridLayout(2,4));
    add(xLeftText);
    add(xRightText);
    add(yCenterText);
    sizeText.setEditable(false);
    add(sizeText);
    add(xLeftTag); add(xRightTag);
    add(yCenterTag); add(sizeTag);
    PointArray.xLeft = oldXLeft = ORIGINAL_XLEFT;
    PointArray.xRight = oldXRight = ORIGINAL_XRIGHT;
    PointArray.yCenter = oldYCenter = ORIGINAL_YCENTER;
    style = new DecimalFormat("0.0#E0"); // for width display
    updatePanel();
  }
  void updatePanel() {
    double xLeft = PointArray.xLeft;
    double xRight = PointArray.xRight;
    double yCenter = PointArray.yCenter;
    xLeftText.setText(Double.toString(xLeft));
    xRightText.setText(Double.toString(xRight));
    yCenterText.setText(Double.toString(yCenter));
    sizeText.setText(style.format(xRight-xLeft));
    if (buttonPanel != null)
      PointArray.loopCount = buttonPanel.getLoopCount();
  }
  void setButtonHandler(ButtonHandler b) {buttonPanel = b;}
  private JLabel xLeftTag = new JLabel("X left");
          JTextField xLeftText = new JTextField(FIELD_WIDTH);
  private JLabel xRightTag = new JLabel("X right");
            JTextField xRightText = new JTextField(FIELD_WIDTH);
  private JLabel yCenterTag = new JLabel("Y center");
          JTextField yCenterText = new JTextField(FIELD_WIDTH);
  private JLabel sizeTag = new JLabel("Width");
          JTextField sizeText = new JTextField(FIELD_WIDTH);
          static final double ORIGINAL_XLEFT = -2.0;
          static final double ORIGINAL_XRIGHT = 0.5;
          static final double ORIGINAL_YCENTER = 0.0;
  private static final int FIELD_WIDTH = 25;
  double oldXLeft, oldXRight, oldYCenter;
  DecimalFormat style;
  private ButtonHandler buttonPanel;
}

class MyCanvas extends JPanel {
  public MyCanvas() {
    super();
  }
  public void setButton(ButtonHandler bp) {
    buttonPanel = bp;
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.drawImage(PointArray.image,null,0,0);
    setCursor(defaultCursor);
    buttonPanel.setCursor(defaultCursor);
    if (PointArray.julia)
      g2.drawString(PointArray.C.toString(),
                    10,getHeight()-10);
  }
  static Color colorArray[];
  static final Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
  static final Cursor waitCursor = new Cursor(Cursor.WAIT_CURSOR);
  ButtonHandler buttonPanel;
}

class MouseHandler extends MouseAdapter
  implements MouseMotionListener {
  public MouseHandler(TextHandler textP, MyCanvas panel, ButtonHandler bp) {
    super();
    p = panel;
    textPanel = textP;
    buttonPanel = bp;
    newJulia = false;
    xOld = -1;
  }
  public void mouseMoved(MouseEvent e) {
  // do nothing
  }
  public void mouseDragged(MouseEvent e) {
    if (newJulia) return;
    x = e.getX(); y = e.getY();
    if (xOld > 0) drawIt(xStart, yStart, xOld, yOld);
    drawIt(xStart, yStart, x, y);
    xOld = x; yOld = y;
  }
  public void mouseReleased(MouseEvent e) {
    double xLeft,xRight,yCenter,size;
    xLeft = PointArray.xLeft;
    xRight = PointArray.xRight;
    yCenter = PointArray.yCenter;
    size = PointArray.size;
    xOld = -1;
    double highb = yCenter +
      (xRight - xLeft) / 2;
    double delta = (xRight - xLeft) / size;
    xLeft = xLeft + rx*delta;
    xRight = xLeft + rw*delta;
    yCenter = highb - (ry + rh/2) * delta;
    p.setCursor(p.waitCursor); buttonPanel.setCursor(p.waitCursor);
    PointArray.xLeft = xLeft;
    PointArray.xRight = xRight;
    PointArray.yCenter = yCenter;
    if (newJulia) {
      PointArray.xJulia = xLeft;
      PointArray.yJulia = yCenter;
      PointArray.julia = true;
      PointArray.xLeft = -1.8;
      PointArray.xRight = 1.8;
      PointArray.yCenter = 0.0;
      newJulia = false;
    }
    textPanel.updatePanel();
    PointArray.computePoints(true);
    p.repaint();
  }
  public void mousePressed(MouseEvent e) {
    xStart = e.getX(); yStart = e.getY();
    if ((e.getModifiersEx() & (InputEvent.BUTTON3_DOWN_MASK
                          |  InputEvent.META_DOWN_MASK))
      != 0) { // right click to initiate Julia set
      newJulia = true;
      rx = xStart;
      ry = yStart;
      rh = 0;
      rw = 1;
    }
  }
  public void drawIt(int x1, int y1, int x2, int y2) {
    g = p.getGraphics();
    y2 = y1 + x2 - x1; // force a square
    rx = x1; ry = y1;
    rw = x2 - x1; rh = y2 - y1;
    if (rw < 0) { rw = -rw; rx = rx - rw; }
    if (rh < 0) { rh = -rh; ry = ry - rh; }
    g.setXORMode(Color.white);
    g.drawRect(rx, ry, rw, rh);
  }
  int xStart, yStart, xOld, yOld, x, y, rx, ry, rw, rh;
  Graphics g;
  MyCanvas p;
  boolean newJulia;
  TextHandler textPanel;
  ButtonHandler buttonPanel;
}

public class MandelbrotApp extends JApplet {
  // class constants
  // initial size of some stuff
  /* Documentation only, window sizes set by HTML
  private static final int WINDOW_WIDTH = 581;
  private static final int WINDOW_HEIGHT = 540;
  */
  private static final int GRAPH_SIZE = 500;
  private static final int WINDOW_LEFT = 10;
  private static final int WINDOW_TOP = 20;
  // instance variables
  // private JFrame window = new JFrame("The Mandelbrot Set");
  private MyCanvas gPanel;

  // constructor
  public void init() {
  /* public MandelbrotGUI {
    window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    window.setLocation(WINDOW_LEFT, WINDOW_TOP);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    */
    TextHandler textPanel = new TextHandler();
    PointArray.setUp(GRAPH_SIZE);
    PointArray.computePoints(true);
    gPanel = new MyCanvas();
    gPanel.setPreferredSize(new Dimension(GRAPH_SIZE,GRAPH_SIZE));
    ButtonHandler buttonPanel = new ButtonHandler(textPanel, gPanel);
    textPanel.setButtonHandler(buttonPanel);
    MouseHandler mouse = new MouseHandler(textPanel,gPanel,buttonPanel);
    gPanel.setButton(buttonPanel);
    gPanel.addMouseListener(mouse);
    gPanel.addMouseMotionListener(mouse);
    // Now set up the window
    Container c = getContentPane();
    c.add(gPanel,BorderLayout.CENTER);
    c.add(textPanel,BorderLayout.SOUTH);
    c.add(buttonPanel,BorderLayout.EAST);
    /*
    window.setVisible(true); // display window
    window.toFront();
    */
    // System.out.println(gPanel.getSize());
  }
}

Valid XHTML 1.0 Strict!