Improved Mandelbrot Applet

Java Applet   Here is our new and improved Mandelbrot applet. It now computes the picture in an off-screen area and displays it in the visible area after each vertical pass. 

Make sure you click on the Control button to change parameters. Notice that the computation will start over as soon as you click on the Apply button. 

Also, switch to the previous page, wait a few seconds, then return to this page. You should notice that the computation goes on in the background even when you are not looking at the page. 




The source code consists of three classes: the main applet, a control dialog, and the drawing canvas. The three classes are listed below: 

Main Applet 

import java.applet.*;
import java.awt.*;

public class TmpMandel extends Applet
{
   private Image  offImage = null;
   private Button button   = new Button("Controls");
   private TmpMandelCanvas   drawing;
   private TmpMandelControl  control;
/* =========================================================== */
   public void init()
   {
      setLayout(new BorderLayout());
      Panel bottomRow = new Panel(); bottomRow.setLayout(new FlowLayout());
      bottomRow.add(button);
      add("South",  bottomRow); 

      int w  = super.size().width;
      int h = super.size().height - bottomRow.preferredSize().height;
      offImage = createImage(width, height);
      drawing = new TmpMandelCanvas (offImage,w, h, -2, 2, -2, 2, 50, 2);
      control = new TmpMandelControl(this,-2,2,-2,2,50,2);
      add("Center", drawing);
   }
/* =========================================================== */
   public void setDrawingParams(double xMin, double xMax,
                                double yMin, double yMax,
                                int    iter, int   pixel)
   {
      drawing.stop();
      drawing.setParams(xMin,xMax,yMin,yMax,iter,pixel);     
      drawing.start();
   }
/* =========================================================== */
    public boolean action(Event e, Object arg)
    {
       if (e.target == button)
       {
          control.show(); control.toFront();
          return true;       
       }
       else 
          return false;
    }
}

Control Dialog 

import java.awt.*;

public class TmpMandelControl extends Frame
{
   private double xMin, xMax, yMin, yMax;
   private int    iter, pixel;

   private NumberFieldPlus xMinField  = new NumberFieldPlus(-2.0,9),
                           xMaxField  = new NumberFieldPlus(2.0,9),
                           yMinField  = new NumberFieldPlus(-2.0,9),
                           yMaxField  = new NumberFieldPlus(2.0,9),
                           iterField  = new NumberFieldPlus(40,5),
                           pixelField = new NumberFieldPlus(2,5),

   Private Button    Okay   = new Button("Okay"),
                     Cancel = new Button("Cancel"),
                     Apply  = new Button("Apply");
   Private TmpMandel master;

   public TmpMandelControl(TmpMandel _master, double _xMin, double _xMax,
                           double _yMin, double _yMax, int _iter, int _pixel)
  {
      super("Mandelbrot Controls");
      master = _master;
      xMinField.setDelta(0.2);  xMaxField.setDelta(0.2);
      yMinField.setDelta(0.2);  yMaxField.setDelta(0.2);
      setParams(_xMin,_xMax,_yMin,_yMax,_iter,_pixel);

      Panel ButtonRow = new Panel();
      ButtonRow.setLayout(new FlowLayout());
      ButtonRow.add(Okay); ButtonRow.add(Apply); ButtonRow.add(Cancel);

      Panel Params = new Panel();
      Params.setLayout(new GridLayout(6,2));
      Params.add(new Label("xMin:"));       Params.add(xMinField);
      Params.add(new Label("xMax:"));       Params.add(xMaxField);
      Params.add(new Label("yMin:"));       Params.add(yMinField);
      Params.add(new Label("yMax:"));       Params.add(yMaxField);
      Params.add(new Label("iterations:")); Params.add(iterField);
      Params.add(new Label("Pixel Size:")); Params.add(pixelField);

      setLayout(new BorderLayout());
      add("Center", Params); add("South", ButtonRow);
   
      validate(); pack(); resize(preferredSize());  
   }
/* =========================================================== */
   public void setParams(double _xMin, double _xMax,
                         double _yMin, double _yMax,
                         int    _iter, int    _pixel)
   {
      xMin  = _xMin; xMax  = _xMax; yMin  = _yMin; yMax  = _yMax;
      iter  = _iter; pixel = _pixel;
      xMinField.setText (String.valueOf(_xMin));
      xMaxField.setText (String.valueOf(_xMax));
      yMinField.setText (String.valueOf(_yMin));
      yMaxField.setText (String.valueOf(_yMax));
      iterField.setText (String.valueOf(_iter));
      pixelField.setText(String.valueOf(_pixel));
   }
/* =========================================================== */
   private void handleOkay()
   {
      xMin  = xMinField.getDouble();  xMax  = xMaxField.getDouble();
      yMin  = yMinField.getDouble();  yMax  = yMaxField.getDouble();
      iter  = iterField.getInteger(); pixel = pixelField.getInteger();
      master.setDrawingParams(xMin,xMax,yMin,yMax,iter,pixel);
   }
/* =========================================================== */
   public boolean handleEvent(Event e)
   {
      if (e.id == Event.WINDOW_DESTROY)
      {
         hide();
         return true;
      }
      else
         return super.handleEvent(e);
   }
/* =========================================================== */
   public boolean action(Event e, Object arg)
   {
      if (e.target == Okay)
      {
         handleOkay(); hide(); return true;
      }
      else if (e.target == Apply)
      {
         handleOkay(); return true;
      }
      if (e.target == Cancel)
      {
         hide(); return true;
      }
      else
         return false;
   }
}

Drawing Canvas 

import java.awt.*;

public class TmpMandelCanvas extends Canvas implements Runnable
{
   private double   xMin, xMax, yMin, yMax;
   private int      iter, pixel;
   private int      width, height;
   private boolean  newImage;

   private Thread   thread   = null;
   private Image    offImage = null;
   private Graphics offGraph = null;

   public TmpMandelCanvas(Image _offImage, int _width,  int _height,
                          double _xMin, double _xMax, double _yMin, double _yMax,
                          int    _iter,   int    _pixel)
   {
      super();
      offImage = _offImage;
      setParams(_xMin,_xMax, _yMin, _yMax, _iter, _pixel);
      width = _width; height = _height;
      resize(width,height);
      offGraph = offImage.getGraphics();
      start();
   }

   public void start()
   {
      if (thread == null)
      {
         offGraph.setColor(Color.white); offGraph.fillRect(0,0,width,height);
         thread = new Thread(this); thread.setPriority(2); thread.start();
      }
   }

   public void stop()
   {
      if (thread != null)
      {
         thread.stop();
         thread = null;
      }
   }

   public void setParams(double _xMin, double _xMax, double _yMin, double _yMax,
                         int    _iter, int    _pixel)
   {
      xMin  = _xMin; xMax  = _xMax; yMin  = _yMin; yMax  = _yMax;
      iter  = _iter; pixel = _pixel;
   }

   static double toWorld(int i, int iMax, double min, double max)
   {
      return (max - min) / iMax * ((double)i - iMax) + max;
   }
   
   static int toScreen(double x, int iMax, double min, double max)
   {
      return (int)Math.round( iMax / (max - min) * (x - max) + iMax);
   }

   public Color computeColor(double a, double b, int maxIter)
   {
      int iter = 0;
      double x = 0, y = 0, tmp = 0;
      boolean escaped = false;

      while ((iter < maxIter) && (!escaped))
      {
         tmp = x*x - y*y + a;
         y = 2*x*y + b;
         x = tmp;
         if ( (Math.abs(x) + Math.abs(y)) >= 5.0)
            escaped = true;
         iter++;
      }
      if (escaped)
         return new Color((20*iter) % 255, 0, 0);
      else
         return Color.blue;
   }

   public void run()
   {
      for (int ix = 0; ix < width; ix += pixel)
      {
         for (int iy = 0; iy < height; iy +=pixel)
         {
            double a = toWorld(ix, width,xMin,xMax);
            double b = toWorld(iy, height, yMax, yMin);
            offGraph.setColor(computeColor(a,b,iter));
            offGraph.fillRect(ix,iy,pixel,pixel);
         }
         repaint();
      }
      stop();
   }

   public void update(Graphics g)
   {
      paint(g);
   }

   public void paint(Graphics g)
   {
      g.drawImage(offImage,0,0,null);
   }
}

(bgw)