Class FractalCanvas

All Implemented Interfaces:
ComponentListener, ImageObserver, MenuContainer, Serializable, EventListener, Accessible

public class FractalCanvas extends JPanel implements ComponentListener
This class performs the drawing of the fractal, using a number of threads/runnables to compute the image in chunks.

Each Runnable has its own local version of Jep, and its own copy of the expression. These are set during construction of the runnable.

 Jep localJep = new Jep(new LightWeightComponentSet(jep));
 ImportationVisitor iv = new ImportationVisitor(localJep);
 Node localExpression = iv.deepCopy(expression);
 Complex localC = new Complex(0, 0); // A single mutable value
 Complex localZ = new Complex(0, 0);
 localJep.addVariable("c", localC);
 localJep.addVariable("z", localZ);
 

These are copies are used in the main evaluation loop.

 localC.set(x, y);
 localZ.set(0, 0);
 int count = 0;
 while (count < iterations && localZ.abs2() < 4.0) {
        localZ.set((Complex) localJep.evaluate(localExpression));
        count++;
 }
 

Scheduling of threads is managed by a ThreadPoolExecutor with 10 threads

 ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(100));
 
When a new image is needed a set of Runnables is created and sent to the executor.
 try {
         for (int i = 0; i < nDivisions; ++i) {
            for (int j = 0; j < nDivisions; ++j) {
          Runnable blockRunner = new CalcBlockRunnable(i * wid, j * high, wid, high);
          Future<Integer> future = executor.submit(blockRunner, wid * high);
          synchronized (futures) {
              futures.add(future);
          }
      }
   }
 } catch (RejectedExecutionException ex) {
     System.out.println("Rejected execution " + ex.toString());
 } catch (JepException ex) {
     System.out.println(ex);
 }
 

Each of these has a Future value used to count the number of completed runnables and cancel the tasks if needed. An ArrayList of these futures is kept. If the screen needs to be redrawn before every task is finished then they are killed by first calling future.cancel(true) and then purging them with executor.purge().

An additional thread is used to collect statistics about completed tasks. A ConcurrentLinkedDeque<Integer> is used to send messages to this queue, and a Lock and Condition is used to wake this thread when there is a new message.

 ConcurrentLinkedDeque<Integer> reportQueue = new ConcurrentLinkedDeque<Integer>();
 Reporter reporter = new Reporter();
 Thread reportThead = new Thread(reporter, "ReportThread");
 reportThead.start();
 // Lock for reporter
 private Lock lock = new ReentrantLock();
 // Condition to signal when the reportQueue has data
 private Condition notEmpty = lock.newCondition();
 

Messages are sent to this queue using

 lock.lock();
 reportQueue.add(nItts);
 notEmpty.signal();
 lock.unlock();
 

And the report thread waits for messages

 lock.lock();
 while(true) {
     while(reportQueue.isEmpty()) {
           notEmpty.awaitUninterruptibly();
     }
     Integer itts =  reportQueue.pollFirst();
     ...
 }
 

The image is created using a MemoryImageSource backed by a shared pixel array.

 int[] pixels = new int[dimensions.width * dimensions.height];
 MemoryImageSource source = new MemoryImageSource(dimensions.width, dimensions.height, pixels, 0, dimensions.width);
 source.setAnimated(true);
 Image outImage = Toolkit.getDefaultToolkit().createImage(source);
 

The pixels are simply set by the runnable pixels[index] = Color.getRGB() and when an entire block is finished image consumers are informed using source.newPixels(rect.x,rect.y,rect.width,rect.height ). The paint method simply uses the Graphic.drawImage using Component imageUpdate() callback method to paint more of the image as it becomes available.

 public void paint(Graphics g) {
     g.drawImage(outImage, 0, 0, this);
 }
 
See Also:
  • Constructor Details

    • FractalCanvas

      public FractalCanvas(String initialExpression, Fractal parent)
      Constructor.
  • Method Details

    • setExpressionString

      public boolean setExpressionString(String newString)
      Parses a new expression
      Returns:
      true on successful parse
    • setColorExpression

      public boolean setColorExpression(String str)
      Parses a new expression for colors
      Returns:
      true on successful parse
    • setIterations

      public void setIterations(int iterations_in)
      Sets number of iterations
      Parameters:
      iterations_in -
    • paint

      public void paint(Graphics g)
      Overrides:
      paint in class JComponent
    • shiftSelection

      public void shiftSelection(com.singularsys.jepexamples.applets.FractalCanvas.RegionListener regionListener)
      Responds to mouse drag events translates the scene.
    • zoomIn

      public void zoomIn(Complex mid)
      Zoom in the sceen
      Parameters:
      mid - midpoint of the new scene
    • zoomOut

      public void zoomOut()
      Zoom out the scene.
    • componentResized

      public void componentResized(ComponentEvent e)
      Specified by:
      componentResized in interface ComponentListener
    • componentMoved

      public void componentMoved(ComponentEvent e)
      Specified by:
      componentMoved in interface ComponentListener
    • componentShown

      public void componentShown(ComponentEvent e)
      Specified by:
      componentShown in interface ComponentListener
    • componentHidden

      public void componentHidden(ComponentEvent e)
      Specified by:
      componentHidden in interface ComponentListener
    • render

      public void render()