Home : Course Map : Chapter 11 : Java : Tech :
Fractal Generation - Drawing
JavaTech
Course Map
Chapter 11

Introduction
Image Class
Image Loading
  Demo 1 Demo 2  
Pixels/Transparency
  Demo 3
Pixel Handling
  Demo 4  
Demo 5
Exercises

    Supplements
Java 2D Imaging
BufferedImage
Creating Buf.Image
Pixel Handling
  Demo 1 Demo 2
Filters
  Convolutions
     Demo 3
  AffineTransforms
     Demo 4
  LookupTable
     Demo 5 Demo 6
  Rescale
     Demo 7
  Color Conversion
     Demo 8
  Custom
     Demo 9
Exercises
Java Adv Imaging
AWT Flicker:
  Override Update
     Demo 1  Demo 2
  Clipping
     Demo 3
  Double Buffer
     Demo 4

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

Here are the DrawFunction subclasses used to create and draw the fractal patterns for the FractalsApplet demonstration program. The algorithms were described earlier.

The key features of the draw() method in DrawBifurcation are the three loops. The outer loop cycles through the columns (i.e. the x horizontal coordinate) of the plot. The first inner loop sets all the points on each column to a fixed background color. The second inner loop implements the heart of the algorithm. The given formula is interated for MAX_ITERATIONS number of times. After skipping an initial set of interations so as to reach the orbit behavior of the iterated values, a color is draw for each row (i.e. point on the y axis) obtained from a scaling of the iterated variable z.

The draw() methods for the other three algorithms also eacj include three key loops but the structure differs. The outer loop cycles over the rows (i.e. x values) and a second nested loop cycles through the columns (i.e. y values). A third loop iterates the function starting from a value determined by the current column, row point (i.e. x_beg, y_beg). After the loop either finishes or "escapes", the final iterated value determines the color at the column, row (i.e. x,y) point.

In all four cases, the drawLine() method in Graphics is used to draw each point of the pattern. This involves many invocatins of drawLine() and an alternative method involving the modification of pixel values in an image is used instead in the next demonstration program to speed up the display.

Note that for the complex numbers, we use the Complex.java class from Chapter 4: Tech. Unlike the immutable complex class discussed in Chapter 5: Tech, this class allows direct access to the real and imaginary data variables. Since the processing here is quite intensive, we decided to take advantage of this capability to obtain faster performance. The immutable class requires method invocations to access the real and imaginary data and also require that new objects be created for every new complex value. These all take up some amount of time, which may or may not be noticable depending on the processor.

Fractal Algorithm Classes used in FractalsApplet

DrawBifurcation.java - DrawFunction subclass that draws fractal pattern generated with the Bifurcation algorithm.
DrawJuliaSet.java - DrawFunction subclass that draws fractal pattern generated with the Julia Set algorithm.
DrawJuliaSetNewton.java - DrawFunction subclass that draws fractal generated pattern with the Julia Set via Newton's method algorithm.
DrawMandlebrot.java - DrawFunction subclass that draws fractal pattern generated with the Mandlebrot algorithm.

+ Previous class:
Chapter 4:Tech: Complex.java

import java.awt.*;

/**
  * Draw patterns with the bifurcation algorithm on a graphics
  * context passed to it.
  *
  * It follows the algorithm as given in "Java Number Cruncher"
  * by R. Mak.
**/
public class DrawBifurcation extends DrawFunction
{
  private static final int MAX_ITERATIONS   = 200;
  private static final int SKIP_INTERATIONS =  50;

  /**
    *  Execute the bifurcation algorithm onto the PlotPanel.
    *
    *  @param g graphics context
    *  @param frame_width display area width in pixels.
    *  @param frame_height display area height in pixels.
    *  @param frame_start_x horizontal point on display where
    *    drawing starts in pixel number.
    *  @param frame_start_y vertical point on display where
    *    drawing starts in pixel number.
    *  @param x_scale 2 dimensional array holding lower and
    *    upper values of the function input scale range.
    *  @param y_scale 2 dimensional array holding lower and
    *    upper values of the function output scale range.
   **/
  public void draw (Graphics g,
                   int frame_start_x, int frame_start_y,
                   int frame_width, int frame_height,
                   double [] x_scale, double [] y_scale) {

    Color save_color = g.getColor ();
    int y_max_index = y_scale.length - 1;

    // Don't draw outside of the frame.
    g.setClip (frame_start_x+1, frame_start_y+1, frame_width-1, frame_height-1);

    // Check if ready to draw the line
    if (fParameters == null) return;

    // Get conversion factors from data scale to frame pixels
    double x_scale_factor = fParameters[0]/frame_width;
    double y_scale_factor = fParameters[1]/frame_height;

    // Coordinates in pixel scale
    int row = 0;
    int col = 0;

    // Outer loop steps through the c values along the horizontal axis.
    for (col = 0; col < frame_width; col++) {
        // Change from pixel scale to the data scale
        double c = x_scale[0] + col * x_scale_factor;
        double z = fParameters[2]; // Pass the starting value.

        // Draw the background color for each column.
        g.setColor (fColor);
        for (row=0; row < frame_height; row++) {
            g.drawLine (col+frame_start_x, row+frame_start_y,
                        col+frame_start_x, row+frame_start_y);
        }

        // Now on this column, plot z along the vertical axis as determined
        // by the function:
        //   z = z*z + c
        // Plot it for MAX_ITERATIONS number of times. However, don't start
        // plotting until after SKIP_ITERATIONS number of times so as to
        // reach the orbit behavior of the function.
        g.setColor (Color.RED);
        for (int i=0; i < MAX_ITERATIONS; i++) {
            z = z*z + c;
            if (i >= SKIP_INTERATIONS) {
                // Convert from data scale to pixel scale
                row = (int)Math.round ((y_scale[y_max_index] - z)/y_scale_factor);

                // Use the drawLine method to plot a point.
                g.drawLine (col+frame_start_x, row+frame_start_y,
                            col+frame_start_x, row+frame_start_y);
            }
        }
    }
    g.setColor (save_color);

  } // draw

} // class DrawBifurcation

  

/**
  * Draw patterns with the Julia set algorithm on a graphics
  * context passed to it.
  *
  * The class follows the algorithm as given in "Java Number Cruncher"
  * by R. Mak.
 **/
public class DrawJuliaSet extends DrawFunction
{
  private static final int MAX_ITERATIONS   = 32;
  private static final int ESCAPE_MAGNITUDE =  2;

  /**
    *  Draw fractal patterns with the Julia set algorithm.
    *
    *  @param g graphics context
    *  @param frame_width display area width in pixels.
    *  @param frame_height display area height in pixels.
    *  @param frame_start_x horizontal point on display where
    *    drawing starts in pixel number.
    *  @param frame_start_y vertical point on display where
    *    drawing starts in pixel number.
    *  @param x_scale 2 dimensional array holding lower and
    *    upper values of the function input scale range.
    *  @param y_scale 2 dimensional array holding lower and
    *    upper values of the function output scale range.
   **/
  public void draw (Graphics g,
                   int frame_start_x, int frame_start_y,
                   int frame_width, int frame_height,
                   double [] x_scale, double [] y_scale) {

    Color save_color = g.getColor ();
    int y_max_index = y_scale.length - 1;

    // Don't draw outside of the frame.
    g.setClip (frame_start_x+1, frame_start_y+1, frame_width-1, frame_height-1);

    // Check if ready to draw the line
    if (fParameters == null) return;

    // Get conversion factors from data scale to frame pixels
    double x_scale_factor = fParameters[0]/frame_width;
    double y_scale_factor = fParameters[1]/frame_height;

    // Obtain the complex constant from the user interface.
    Complex c = new Complex (fParameters[2], fParameters[3]);
    Complex z = new Complex (0.0,0.0);

    // Coordinates in pixel scale
    int row = 0;
    int col = 0;

    // Outer loop steps through the c value along the horizontal axis.
    for (row = 0; row < frame_height; row++) {
        // Change from pixel scale to the data scale
        double y_beg = y_scale[y_max_index] - row * y_scale_factor;

        for (col = 0; col < frame_width; col++) {
          double x_beg = x_scale[0] + col * x_scale_factor;
          z.real = x_beg; z.img = y_beg; // Set the starting value.

          boolean escaped = false;
          int iter   = 0;
          double x   = x_beg;
          double y   = y_beg;
          double mag = 0.0;

          // Iterate the function:
          //   z = z*z + c
          do {
              z.multiply (z);
              z.add (c);
              mag = z.modulus ();
              escaped = mag > ESCAPE_MAGNITUDE;
              ++iter;
          } while (iter < MAX_ITERATIONS && (!escaped));

          // If escaped, then set color to a shade of gray
          // proportional to the number of interations. The
          // more iterations, the darker
          if (escaped) {
              int gray = 0xFF - (0xFF*iter)/MAX_ITERATIONS;
              gray = Math.min (gray, 240);
              g.setColor (new Color (gray, gray, gray));
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);

          } else {

          // Did not escape so relate the color instead to the
          // final magnitude of z.

              int i = ((int)(100*mag)) / ESCAPE_MAGNITUDE + 1;
              int red = (425*i) & 0xFF;
              int grn = (375*i) & 0xFF;
              int blu = (325*i) & 0xFF;

              g.setColor (new Color (red,grn,blu));
              // Use the drawLine method to plot a point.
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);
          }
        }
    }
    g.setColor (save_color);

  } // draw

} // class DrawJuliaSet

  
import java.awt.*;

/**
  * Draw patterns with the Julia set via Newton's method.

  *
  * Follow the algorithm given in "Java Number Cruncher"
  * by R. Mak for solving f(z) = z^3 - 1 with Newton's algorithm.
  *
**/
public class DrawJuliaSetNewton extends DrawFunction
{
  private static final int MAX_ITERATIONS   = 100;

  // Create two complex constants needed in the algorthm.
  private static final Complex Z_ONE   = new Complex (1,0);
  private static final Complex Z_THREE = new Complex (3,0);

  // Create a pool of complex numbers to use in the algorithm
  private Complex fZ0 = new Complex (0.0, 0.0);
  private Complex fZ2 = new Complex (0.0, 0.0);
  private Complex fZ3 = new Complex (0.0, 0.0);

  /**
    *
    *  Create fractal patterns with the Julia set from Newton's
    *  method algorithm and draw them with the graphics context.
    *
    *  @param g graphics context
    *  @param frame_width display area width in pixels.
    *  @param frame_height display area height in pixels.
    *  @param frame_start_x horizontal point on display where
    *    drawing starts in pixel number.
    *  @param frame_start_y vertical point on display where
    *    drawing starts in pixel number.
    *  @param x_scale 2 dimensional array holding lower and
    *    upper values of the function input scale range.
    *  @param y_scale 2 dimensional array holding lower and
    *    upper values of the function output scale range.
   **/
  public void draw (Graphics g,
                   int frame_start_x, int frame_start_y,
                   int frame_width, int frame_height,
                   double [] x_scale, double [] y_scale) {

    Color save_color = g.getColor ();
    int y_max_index = y_scale.length - 1;

    // Don't draw outside of the frame.
    g.setClip (frame_start_x+1, frame_start_y+1, frame_width-1, frame_height-1);

    // Check if ready to draw the line
    if (fParameters == null) return;

    // Get conversion factors from data scale to frame pixels
    double x_scale_factor = fParameters[0]/frame_width;
    double y_scale_factor = fParameters[1]/frame_height;

    // Create our complex variable for the algorithm.
    Complex z  = new Complex (0.0,0.0);

    // Coordinates in pixel scale
    int row = 0;
    int col = 0;

    // Outer loop steps through the c value along the horizontal axis.
    for (row = 0; row < frame_height; row++) {
        // Change from pixel scale to the data scale
        double y_beg = y_scale[y_max_index] - row * y_scale_factor;

        for (col = 0; col < frame_width; col++) {
          double x_beg = x_scale[0] + col * x_scale_factor;

          // Create the z for this point on the complex plane
          z.real = x_beg; z.img = y_beg; // Set the starting value.

          int iter   = 0;

          // Iterate the function:
          //   z = z*z*z - 1
          do {
              // Use the pool complex objects to hold intermediate values
              fZ0.real = z.real; fZ0.img = z.img; // save z in fZ0
              z.multiply (z);  // z = z * z
              fZ2.real = z.real; fZ2.img = z.img; // save z*z in fZ2
              z.multiply (fZ0);// z = z * z * z
              fZ3.real = z.real; fZ3.img = z.img; // save z*z*z in fZ3

              // Now comute the iteration formula from Newton's method.
              //     (z^3 -1.0)/(3.0 * z^2)
              fZ3.subtract (Z_ONE); // fZ3 = z*z*z - 1
              fZ2.multiply (Z_THREE); // fZ2 = 3.0 * z*z
              z.real = fZ3.real; z.img = fZ3.img;
              z.divide (fZ2); // z = (z^3 - 1.0)/(3.0 * z^2)

              ++iter;
          } while (iter < MAX_ITERATIONS && (!z.equals (fZ0)) );

          // Set color proportional to the number of interations.
          int colorComp = 20 * (iter%10);

          if (z.real > 0.0) { // root = 1
              g.setColor (new Color (colorComp, colorComp, 0xFF));
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);
          } else
          if (z.img > 0.0) { // root ~= -0.5 + .9i
              g.setColor (new Color (colorComp, 0xFF, colorComp));
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);
          } else { // root ~= -0.5 - 0.9i
              g.setColor (new Color (0xFF, colorComp, colorComp));
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);
          }
        }
    }
    g.setColor (save_color);
  } // draw

} // class DrawJuliaSetNewton

  
import java.awt.*;

/**
  * Draw patterns with the Mandlebrot algorithm.

  *
  * Follows the algorithm given in "Java Number Cruncher"
  * by R. Mak for solving f(z) = z^3 -1 with Newton's
  * algorithm.
**/
public class DrawMandlebrot extends DrawFunction
{
  private static final int MAX_ITERATIONS   = 32;
  private static final int ESCAPE_MAGNITUDE =  2;

  // Create two complex constants needed in the algorthm.
  private static final Complex Z_ONE   = new Complex (1,0);
  private static final Complex Z_THREE = new Complex (3,0);

  // Create a pool of complex numbers to use in the algorithm
  private Complex fZ0 = new Complex (0.0, 0.0);
  private Complex fZ2 = new Complex (0.0, 0.0);
  private Complex fZ3 = new Complex (0.0, 0.0);

  /**
    *  Create fractal patterns with the Mandlebrot
    *  algorithm and draw them with the graphics context.
    *
    *  @param g graphics context
    *  @param frame_width display area width in pixels.
    *  @param frame_height display area height in pixels.
    *  @param frame_start_x horizontal point on display where
    *    drawing starts in pixel number.
    *  @param frame_start_y vertical point on display where
    *    drawing starts in pixel number.
    *  @param x_scale 2 dimensional array holding lower and
    *    upper values of the function input scale range.
    *  @param y_scale 2 dimensional array holding lower and
    *    upper values of the function output scale range.
   **/
  public void draw (Graphics g,
                   int frame_start_x, int frame_start_y,
                   int frame_width, int frame_height,
                   double [] x_scale, double [] y_scale) {

    Color save_color = g.getColor ();
    int y_max_index = y_scale.length - 1;

    // Don't draw outside of the frame.
    g.setClip (frame_start_x+1, frame_start_y+1, frame_width-1, frame_height-1);

    // Check if ready to draw the line
    if (fParameters == null) return;

    // Get conversion factors from data scale to frame pixels
    double x_scale_factor = fParameters[0]/frame_width;
    double y_scale_factor = fParameters[1]/frame_height;

    // Create our complex variable for the algorithm.
    Complex z  = new Complex (0.0,0.0);
    Complex c  = new Complex (0.0,0.0);

    // Coordinates in pixel scale
    int row = 0;
    int col = 0;

    // Outer loop steps through the c value along the horizontal axis.
    for (row = 0; row < frame_height; row++) {
        // Change from pixel scale to the data scale
        double y_beg = y_scale[y_max_index] - row * y_scale_factor;

        for (col = 0; col < frame_width; col++) {
          double x_beg = x_scale[0] + col * x_scale_factor;

          // Create the c for this point on the complex plane
          c.real = x_beg; c.img = y_beg; // Set the complex constant
          z.real = 0.0;   z.img = 0.0;   // Initialize z to 0 + 0i.

          boolean escaped = false;
          int iter   = 0;
          double x   = x_beg;
          double y   = y_beg;
          double mag = 0.0;

          // Iterate the function:
          //   z = z*z + c
          do {
              z.multiply (z);
              z.add (c);
              mag = z.modulus ();
              escaped = mag > ESCAPE_MAGNITUDE;
              ++iter;
          } while (iter < MAX_ITERATIONS && (!escaped));

          // If escaped, then set color to a shade of gray
          // proportional to the number of interations. The
          // more iterations, the darker
          if (escaped) {
              int gray = 0xFF - (0xFF*iter)/MAX_ITERATIONS;
              gray = Math.min (gray, 240);
              g.setColor (new Color (gray, gray, gray));
              g.drawLine (col+frame_start_x, row+frame_start_y,
                        col+frame_start_x, row+frame_start_y);

          } else {

          // Did not escape so relate the color instead to the
          // final magnitude of z.

              int i = ((int)(100*mag)) / ESCAPE_MAGNITUDE + 1;
              int red = (100*i) & 0xFF;
              int grn = (150*i) & 0xFF;
              int blu = (200*i) & 0xFF;

              g.setColor (new Color (red,grn,blu));
              // Use the drawLine method to plot a point.
              g.drawLine (col+frame_start_x, row+frame_start_y,
                          col+frame_start_x, row+frame_start_y);
           }
        }
    }
    g.setColor (save_color);
  } // draw

} // class DrawMandlebrot

  

 

 

Most recent update: June 25, 2005

              Tech
Fractals
Fractal Drawing
   Demo 1
Fractal Draw Code
Fractal Images
  Demo 2
Image Processing
  Demo 3
Histogram Image
  Demo 4
Exercises

           Physics
Calibration/SysError
SimWithCal/SysErr
  Demo 1
Analysis
  Demo 2
Examples

Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.