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

The demonstration program FractalsApplet, shown below, displays a fractal pattern generated by an algorithm selected from a JList. The user can drag the cursor across a given area to obtain a rectangle that will be expanded over the full display panel area. This lets the user see the repetition of the patterns at finer and finer scaling, thus illustrating a leading attribute of fractal behavior.

The routines involved in the applet are listed below. The MousePlotPanel subclass of PlotPanel adds the zoom in capability with the mouse. The fractal algorithms are implemented in DrawFunction subclasses and their code is given on the next page. A graphics context is passed to their draw() method and the drawLine() method in the Graphics class is used to draw each point in the fractal pattern.

In a later page, we discuss a technique in which pixels of an image are modified to create the pattern and then the image is drawn. This provides faster performance.


Demonstration: Fractal Generation Display using Drawing Methods.

FractalsApplet.java - Display a fractal algorithm selected from a list. The X,Y text fields display the dimensions of the area in which the fractal generation occurs. The user can drag the cursor to create a rectangle whose position and dimensions are used to generate a new fractal pattern. The new dimensions are displayed in the X,Y text fields.

The P0, P1 text fields are the parameter settings for the particular algorithm. One or more of these are inactive depending on the algorithm.

The Reset button returns the dimenstions to the default settings and redraws the pattern using the current algorithm.

+ New classes:

FractalController.java
- interface with three methods used to pass information between the parent class and FractalPanel: setFractalParameters(), getFractalParameters() and public void setBusy().
FractalsApplet implements this interface.

MousePlotPanel.java - This abstract subclass of PlotPanel adds the capability for the user to drag open a rectangle on the plot. It implements the MouseHandler interface. The subclass must override the method handleMouseInfo(), which is invoked after the cursor is dragged and the button released.

MouseHandler.java - interface with five methods to provide responses to mouse events on a plot. It is implemented by the MousePlotPanel

FractalPanel.java
- This class overrides the abstract method handleMouseInfo() in MousePlotPanel. The method obtains the new dimenstions from the rectangle created by dragging the cursor. It invokes getScaling() to reset the scaling along the axes, it calls back to the parent class (which implements the FractalController interface) to send the new dimensions, tells the parent that it is busy with the fractal generation, and invokes repaint().

In paintContents() it uses the DrawFunction object corresponding to the selected algorithm to draw the fractal pattern. After the drawing is finished, it calls back the parent class, using the FractalContrller setBusy() method, to announce that it is no longer busy.

A reference to the parent FractalController is passed in the constructor, along with the default dimensions and the DrawFunction object.

The setAlgorithm (DrawFunction drawFractal) method is used to change the current algorithm.

The setRange() method allows the parent to change the dimensions of the plot.

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

+ Previous classes:
Chapter 4:Tech: Complex.java
Chapter 6:Tech: PlotPanel.java, PlotFormat.java

Chapter 6:Tech: DrawFunction.java

// Begin from StarterJApplet11

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;

/**
  *  Demonstrate various fractal algorithms. User selects
  *  from a list of algorithms. Graphical output of the
  *  algorithm is displayed within a plot frame using a
  *  subclass of MousePlotPanel, which in turn is a subclass
  *  of PlotPanel.
  *
  *  The fractal algorithms are included in DrawFunction
  *  subclasses. They create the fractal patterns by drawing
  *  the patters via the draw methods for the graphics context
  *  object passed to it.
  *
  *  The user can drag the cursor to outline a rectangle
  *  whose dimensions are used for a rescaled graph. The
  *  MousePlotPanel provides the mouse event information.
  *
  *  The class mplements the FractalController interface so that the
  *  FractalPanel object can call back to send new dimensions
  *  for the graph area. It also calls back to indicate when
  *  fractal generation is happening so that the controls
  *  can be put into a busy state and not interfere with the
  *  operation.
 **/
public class FractalsApplet extends JApplet
       implements ActionListener, ListSelectionListener,
                  FractalController
{
  // Flag to indicate if the applet is in a browser
  // or running as an application via the main () method.
  boolean fInBrowser = true;

  // Fractal panel can display different algorithms by
  // passing to it a new DrawFractal object.
  FractalPanel fOutputPanel;

  // A text field for input strings
  JTextField fFieldXLeft, fFieldXRite, fFieldYTop, fFieldYBot;

  // Parameters for the algorithms.
  double fParam0 = 0.0;
  double fParam1 = 0.0;

  // Text fields to input the algorithm parameters
  JTextField fFieldParam0;
  JTextField fFieldParam1;

  // Labels for the parameters
  JLabel fLabelParam0, fLabelParam1;

  // A number to indicate the selected algorithm.
  int fAlgorithm = ALG_BIFURCATION;

  // Define a set of constants to specify a given algorithm
  final static int ALG_BIFURCATION   = 0;
  final static int ALG_JULIASET      = 1;
  final static int ALG_JULIASET_NEWT = 2;
  final static int ALG_MANDLEBROT    = 3;

  // Default dimensions according to the algorithm
  double [][] fXYDef =
      {
         {-2.0, 2.0, -2.0, 2.0}, // Bifurcation
         {-2.0, 2.0, -2.0, 2.0}, // Julia set
         {-3.0, 3.0, -3.0, 3.0}, // Julia set from Newton's method
         {-2.5, 2.5, -2.5, 2.5}  // Mandlebrot
      };


  // Hold the default algorithm parameters in this 2-d array.
  double [][] fAlgorithmParams = {
                                  {0.0,  0.0}, // Bifurcation
                                  {0.36, 0.25},// Julia set
                                  {0.0,  0.0}, // Julia set from Newton's method
                                  {0.0,  0.0}  // Mandlebrot
                                 };

  // List component for the algorithms.
  JList fAlgorithmJList;
  String [] fAlgorithmList = {"Bifurcation",
                              "Julia Set",
                              "Julia Set- Newton's",
                              "Mandlebrot"
                             };


  // Keep an array of the fractal generators
  DrawFunction [] drawFractals = new DrawFunction[4];

  // Need a button to return to the default dimensions.
  JButton fResetButton;

   /**
     * Create a User Interface with four fields that show the
     * current dimensions of the fractal graphing area.
     * The interface also includes a JList with the
     * list of algorithms. Selecting one will initiate the
     * algorithm. A "Reset" button will return the dimensions to
     * the default values and redraw the graph.
    **/
  public void init () {

    JPanel panel = new JPanel (new BorderLayout ());

    // When user hits reset button, the graph will be
    // redrawn with default dimensions.
    fResetButton = new JButton ("Reset");
    fResetButton.addActionListener (this);

    // Use a textfield for an input parameter.
    fFieldXLeft = new JTextField (Double.toString (fXYDef[0][0]), 6);
    fFieldXRite = new JTextField (Double.toString (fXYDef[0][1]), 6);
    fFieldYBot  = new JTextField (Double.toString (fXYDef[0][2]),  6);
    fFieldYTop  = new JTextField (Double.toString (fXYDef[0][3]),  6);

    // Use a GridLayout to show the dimensions
    JPanel dim_labels_panel = new JPanel (new GridLayout (2,1));
    dim_labels_panel.add (new JLabel ("X = ", SwingConstants.RIGHT));
    dim_labels_panel.add (new JLabel ("Y = ", SwingConstants.RIGHT));

    JPanel dimensions_panel = new JPanel (new GridLayout (2,2));
    dimensions_panel.add (fFieldXLeft);
    dimensions_panel.add (fFieldXRite);
    dimensions_panel.add (fFieldYBot);
    dimensions_panel.add (fFieldYTop);

    // Parameters fields
    fFieldParam0 = new JTextField (Double.toString (fParam0),6);
    fFieldParam1 = new JTextField (Double.toString (fParam1),6);
    JPanel parameters_panel = new JPanel (new GridLayout (2,2));
    fLabelParam0 = new JLabel ("P0 = ", SwingConstants.RIGHT);
    parameters_panel.add (fLabelParam0);
    parameters_panel.add (fFieldParam0);
    fLabelParam1 = new JLabel ("P1 = ", SwingConstants.RIGHT);
    parameters_panel.add (fLabelParam1);
    parameters_panel.add (fFieldParam1);

    // Combine the panels for the dimensions, their labels
    // and parameters  onto one panel.
    JPanel dim_param_panel = new JPanel ();
    dim_param_panel.add (dim_labels_panel);
    dim_param_panel.add (dimensions_panel);
    dim_param_panel.add (parameters_panel);

    // Create a JList for the algorithms.
    fAlgorithmJList = new JList (fAlgorithmList);

    // Show only the current algorithm
    fAlgorithmJList.setVisibleRowCount (2);

    // Allow only one of the items to be selected.
    fAlgorithmJList.setSelectionMode (
      ListSelectionModel.SINGLE_SELECTION);

    // Select initially the top item.
    fAlgorithmJList.setSelectedIndex (ALG_BIFURCATION);

    // Send selection events to this object.
    fAlgorithmJList.addListSelectionListener (this);

    // Add the list to a JScrollPane so that we can
    // scroll to view other items.
    JScrollPane list_scroll_pane = new JScrollPane (fAlgorithmJList);

    // Create a panel to hold the controls.
    JPanel control_panel = new JPanel ();
    control_panel.add (dim_param_panel);
    control_panel.add (list_scroll_pane);
    control_panel.add (fResetButton);

    // And combine it with the FractalPanel.
    panel.add (control_panel,"South");

    // Create the bifurcation algorithm as the first choice.
    drawFractals[ALG_BIFURCATION] = new DrawBifurcation ();

    // Only need one parameter for the bifurcation algorithm
    fLabelParam1.setEnabled (false);
    fFieldParam1.setEnabled (false);

    // Add the algorithm panel here.
    fOutputPanel = new FractalPanel (this, drawFractals[0],
                                     fXYDef[fAlgorithm][0],
                                     fXYDef[fAlgorithm][1],
                                     fXYDef[fAlgorithm][2],
                                     fXYDef[fAlgorithm][3]);

    panel.add (fOutputPanel,"Center");

    // Then add this panel to the applet.
    add (panel);

  } // init

  /** Callback from the FractalPanel with the new dimensions.**/
  public void setFractalParameters (double x_min, double x_max,
                                    double y_min, double y_max) {
    // Reset dimensions to defaults.
    fFieldXLeft.setText (PlotFormat.getFormatted (x_min, 100.0, 1000.0, 3));
    fFieldXRite.setText (PlotFormat.getFormatted (x_max, 100.0, 1000.0, 3));
    fFieldYBot.setText  (PlotFormat.getFormatted (y_min, 100.0, 1000.0, 3));
    fFieldYTop.setText  (PlotFormat.getFormatted (y_max, 100.0, 1000.0, 3));
  } // setFractalParameters


  /**
    * Implementation of FractalController interface: This method
    * provides the values in the text fields for the parameters
    * needed by a given algorithm.
   **/
  public void getFractalParameters (double [] parameters) {

    if (fAlgorithm == ALG_BIFURCATION) {
       try {
         fParam0 = Double.valueOf (fFieldParam0.getText ());
       } catch (NumberFormatException nfe) {
         fParam0 = fAlgorithmParams[ALG_BIFURCATION][0];
         fFieldParam0.setText ("0.0");
       }
       parameters[2] = fParam0;

    } else
    if (fAlgorithm == ALG_JULIASET) { // Julia Set
       try {
         fParam0 = Double.valueOf (fFieldParam0.getText ());
         fParam0 = Double.valueOf (fFieldParam0.getText ());
       } catch (NumberFormatException nfe) {
         fParam0 = fAlgorithmParams[ALG_JULIASET][0];
         fParam1 = fAlgorithmParams[ALG_JULIASET][1];
         fFieldParam0.setText (Double.toString (fParam0));
       }
       parameters[2] = fParam0;
       parameters[3] = fParam1;
    } else
    if (fAlgorithm == ALG_JULIASET_NEWT ||
        fAlgorithm == ALG_MANDLEBROT ) { // Julia Set - Newton's method
                                         // or Mandlebrot
    }

  } // getFractalParameters


  /**
    * Implementation of FractalController interface: This method
    * turns off controls while fractal generation runs. It turns
    * them back on when the generation is done.
   **/
  public void setBusy (boolean flag) {

    if (flag) {
        fResetButton.setEnabled (false);
        fAlgorithmJList.setEnabled (false);
        if (fAlgorithm == ALG_BIFURCATION ||
            fAlgorithm == ALG_JULIASET) {
           fFieldParam0.setEnabled (false);
           fLabelParam0.setEnabled (false);
        }
        if (fAlgorithm == ALG_JULIASET) {
            fFieldParam1.setEnabled (false);
            fLabelParam1.setEnabled (false);
        }
        fResetButton.setText ("BUSY");
    } else {
        fResetButton.setEnabled (true);
        fAlgorithmJList.setEnabled (true);
        if (fAlgorithm == ALG_BIFURCATION ||
            fAlgorithm == ALG_JULIASET) {
           fFieldParam0.setEnabled (true);
           fLabelParam0.setEnabled (true);
        }
        if (fAlgorithm == ALG_JULIASET) {
            fFieldParam1.setEnabled (true);
            fLabelParam1.setEnabled (true);
        }
        fResetButton.setText ("Reset");
    }
  } // setBusy


  /**
    * Reset button will reset the drawing area back to
    * the default dimensions.
   **/
  public void actionPerformed (ActionEvent e)
  {
    Object source = e.getSource ();
    if (source == fResetButton) {
        // Reset dimensions to defaults
        resetDefaultFrame ();
        fOutputPanel.setRange (fXYDef[fAlgorithm][0],
                               fXYDef[fAlgorithm][1],
                               fXYDef[fAlgorithm][2],
                               fXYDef[fAlgorithm][3]);
    }
  } // actionPerformed


  /** Reset the frame to the default dimensions. **/
  public void resetDefaultFrame ()
  {
     // Reset dimensions to defaults
    fFieldXLeft.setText (Double.toString (fXYDef[fAlgorithm][0]));
    fFieldXRite.setText (Double.toString (fXYDef[fAlgorithm][1]));
    fFieldYBot.setText  (Double.toString (fXYDef[fAlgorithm][2]));
    fFieldYTop.setText  (Double.toString (fXYDef[fAlgorithm][3]));
  } // resetDefaultFrame


  /**
    * User selects the algorithm from the JList.
   **/
  public void valueChanged (ListSelectionEvent evt)  {

    // Get the reference to the JList object
    JList source =  (JList)evt.getSource ();

    // and get an array of the selected items.
    Object [] values = source.getSelectedValues ();

    // In this case only one value can be selected
    // so just look at first item in array.
    String algorithm_selected =  (String)values[0];

    // Set the title accordingly
    fOutputPanel.setTitle (algorithm_selected);

    // For the selected algorithm, pass it to the FractalPanel
    // and also set up the parameter elements on the user interface.

    if (algorithm_selected.equals (fAlgorithmList[0])) {
        // Only need one parameter for the bifurcation algorithm
        fLabelParam1.setEnabled (false);
        fFieldParam1.setEnabled (false);
        fAlgorithm = ALG_BIFURCATION;
        fParam0 = fAlgorithmParams[fAlgorithm][0];
        fFieldParam0.setText (Double.toString (fParam0));
        fFieldParam1.setText (" ");
    } else
    if (algorithm_selected.equals (fAlgorithmList[1])) {
        // Need two parameters for the Julia set algorithm
        fLabelParam1.setEnabled (true);
        fFieldParam1.setEnabled (true);
        fAlgorithm = ALG_JULIASET;
        fParam0 = fAlgorithmParams[fAlgorithm][0];
        fParam1 = fAlgorithmParams[fAlgorithm][1];
        fFieldParam0.setText (Double.toString (fParam0));
        fFieldParam1.setText (Double.toString (fParam1));
        // Get the algorithm
        if (drawFractals[fAlgorithm] == null)
           drawFractals[fAlgorithm] = new DrawJuliaSet ();
        // Pass the new algorithm to the panel
        fOutputPanel.setAlgorithm (drawFractals[fAlgorithm]);
        // Reseting the range will cause the panel to redraw
        // itself with the new algorithm.
        fOutputPanel.setRange (fXYDef[fAlgorithm][0],
                               fXYDef[fAlgorithm][1],
                               fXYDef[fAlgorithm][2],
                               fXYDef[fAlgorithm][3]);
    } else
    if (algorithm_selected.equals (fAlgorithmList[2])) {
        // No parameters used for the Julia set from Newton's method
        fLabelParam0.setEnabled (false);
        fFieldParam0.setEnabled (false);
        fLabelParam1.setEnabled (false);
        fFieldParam1.setEnabled (false);
        fAlgorithm = ALG_JULIASET_NEWT;
        fFieldParam0.setText (" ");
        fFieldParam1.setText (" ");
        // Get the algorithm
        if (drawFractals[fAlgorithm] == null)
           drawFractals[fAlgorithm] = new DrawJuliaSetNewton ();
    } else
    if (algorithm_selected.equals (fAlgorithmList[3])) {
        // No parameters used for the Mandlebrot algorithm
        fLabelParam0.setEnabled (false);
        fFieldParam0.setEnabled (false);
        fLabelParam1.setEnabled (false);
        fFieldParam1.setEnabled (false);
        fAlgorithm = ALG_MANDLEBROT;
        fFieldParam0.setText (" ");
        fFieldParam1.setText (" ");
        // Get the algorithm
        if (drawFractals[fAlgorithm] == null)
           drawFractals[fAlgorithm] = new DrawMandlebrot ();
    }

    // Reset back to the default frame scale
    resetDefaultFrame ();
    // Pass the new algorithm to the panel
    fOutputPanel.setAlgorithm (drawFractals[fAlgorithm]);
    // Reseting the range will cause the panel to redraw
    // itself with the new algorithm.
    fOutputPanel.setRange (fXYDef[fAlgorithm][0],
                           fXYDef[fAlgorithm][1],
                           fXYDef[fAlgorithm][2],
                           fXYDef[fAlgorithm][3]);


  } // valueChanged

  /** Optional application mode. **/
  public static void main (String[] args)  {
    // Frame size.
    int frame_width  = 550;
    int frame_height = 300;

    // Add an instance of this applet to the frame
    FractalsApplet applet = new FractalsApplet ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Draw Fractals");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.add (applet);
    f.setSize (new Dimension (frame_width,frame_height));
    f.setVisible (true);

  } // main

} // class FractalsApplet

  
/**
  * Interface to allow callbacks from the fractal panel
  * to the controller.
**/
public interface FractalController {
  // Pass display area dimensions.
  public void setFractalParameters (double x_min, double x_max,
                                    double y_min, double y_max);
  public void getFractalParameters (double [] parameters);
  public void setBusy (boolean flag);

} // FractalController

  
import java.awt.*;
import java.awt.event.*;

/**
  * This subclass of PlotPanel adds the capability for the user to drag
  * open a rectangle on the plot. The subclass must override the method
  * handleMouseInfo().
**/
public abstract class MousePlotPanel extends PlotPanel
                implements MouseHandler
{
  // Flag if rectangle is within the frame bounds.
  boolean fRectValid = false;

  // The coords for the corners of the dragged rectangle
  int fXMouseStart, fYMouseStart;
  int fXMouseEnd,   fYMouseEnd;

  // Position of cursor when mouse clicked.
  int fXMouseClicked, fYMouseClicked;

  // Rectangle color
  Color fRectColor = Color.BLACK;

  // Color for XOR mode.
  Color fXORColor = Color.WHITE;

  // Flags to indicate what mouse information to post.
  public final static int RECT_DRAGGING = 1;
  public final static int RECT_DONE     = 2;


  /**
    * The constructor creates mouse listeners to
    * send the mouse events to this object.
   **/
  public MousePlotPanel () {

    // Send mouse events to this panel
    addMouseListener (
      new MouseAdapter ()
      {
        public void mousePressed (MouseEvent evt) {
          pressedOnPlot (evt);
        }

        public void mouseClicked (MouseEvent evt) {
          clickedOnPlot (evt);
        }

        public void mouseReleased (MouseEvent evt) {
          releasedOnPlot (evt);
        }
      }
    );

    addMouseMotionListener(
      new MouseMotionAdapter()
      {
        public void mouseDragged (MouseEvent evt) {
          draggedOnPlot(evt);
        }
      }
    );
  } // ctor

  /**
    * Get the starting point for the rectangle
    * when the mouse button is pressed.
   **/
  public void pressedOnPlot (MouseEvent evt) {

    // Get start coords of cursor
    fXMouseStart = evt.getX ();
    fYMouseStart = evt.getY ();

    // Just started so put neg values in stop coords
    fXMouseEnd = -1;
    fYMouseEnd = -1;
  } // pressedOnPlot


  public void clickedOnPlot (MouseEvent evt) {
    // Get coords of cursor
    fXMouseClicked = evt.getX ();
    fYMouseClicked = evt.getY ();
  } // clickedOnPlot


  /** The rectangle opening is finished. Now
    * execute the desired operation with it.
   **/
  public void releasedOnPlot (MouseEvent evt) {
    if (fRectValid) handleMouseInfo (RECT_DONE);
  } // releasedOnPlot


  public void draggedOnPlot (MouseEvent evt) {

    fRectValid = false;

    // Don't start rectangle outside of the frame box
    if (fXMouseStart < fFrameX ||
        fXMouseStart >= (fFrameX + fFrameWidth)) return;
    if (fYMouseStart < fFrameY ||
        fYMouseStart >= (fFrameY + fFrameHeight)) return;

    fRectValid = true;

    // Need a graphics context to draw on the panel.
    Graphics g = getGraphics ();

    // Set the pen color
    g.setColor (fRectColor);

    g.setXORMode (fXORColor);

    // First erase the old rectangle before drawing the new one
    if ((fXMouseEnd != -1) && (fYMouseEnd != -1))
       g.drawRect (Math.min (fXMouseStart, fXMouseEnd),
                   Math.min (fYMouseStart, fYMouseEnd),
                   Math.abs (fXMouseStart - fXMouseEnd),
                   Math.abs (fYMouseStart - fYMouseEnd)
                  );

    // Get current coords of cursor
    fXMouseEnd = evt.getX();
    fYMouseEnd = evt.getY();


    // Don't allow rectangle to extend outside of the frame box
    if (fXMouseEnd < fFrameX) fXMouseEnd = fFrameX;
    if (fXMouseEnd > (fFrameX + fFrameWidth)) fXMouseEnd = fFrameX + fFrameWidth;
    if (fYMouseEnd < fFrameY) fYMouseEnd = fFrameY;
    if (fYMouseEnd > (fFrameY + fFrameHeight)) fYMouseEnd = fFrameY + fFrameHeight;

    // Post the new rectangle info
    handleMouseInfo (RECT_DRAGGING);

    // Draw the new rectangle
    g.drawRect (Math.min (fXMouseStart, fXMouseEnd),
                Math.min (fYMouseStart, fYMouseEnd),
                Math.abs (fXMouseStart - fXMouseEnd),
                Math.abs (fYMouseStart - fYMouseEnd)
               );

    // Release the graphics resources.
    g.dispose ();
  } // draggedOnPlot

  /**
    * Receive the status info on the mouse and
    * do something with it.
   **/
  public abstract void handleMouseInfo (int flag);

} // MousePlotPanel

  
import java.awt.*;
import java.awt.event.*;

/**
  * Interface for a plot that needs to obtain
  * mouse events occuring on it.
 **/
public interface MouseHandler
{

  public void pressedOnPlot (MouseEvent evt);
  public void clickedOnPlot (MouseEvent evt);
  public void releasedOnPlot (MouseEvent evt);
  public void draggedOnPlot (MouseEvent evt);
  public void handleMouseInfo (int flag);

} // MouseHandler

  
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;

/**
  *  This panel executes a given fractal algorithm to generate a
  *  fractal display. It is a subclass of MousePlotPanel and PlotPanel.
  *  PlotPanel provides a framed area with text scale values along the
  *  axes.
  *
  *  MousePlotPanel adds mouse event handling methods that
  *  allow the user to drag a rectangle open. The frame will be
  *  rescaled to the dimensions of this rectangle and the algorithm
  *  re-run within that area.
  *
  *  A reset button allows the user to return to the initial dimensions.
 **/
public class FractalPanel extends MousePlotPanel
{
  // Call back to the controller with new dimensions when
  // user rescales with a new rectangular area.
  FractalController fController;

  // Limit values to x and y axes in data units.
  double fXDataMin, fXDataMax;
  double fYDataMin, fYDataMax;

  // Parameters for x and y scales.
  int fNumXScaleValues = 2;
  int fNumYScaleValues = 2;
  double [] fYScaleValue = new double [2];
  double [] fXScaleValue = new double [2];

  String fTitle = "Fractal Display";


  // Need the fractal drawing function and
  // a parameters array to pass to it.
  DrawFunction fDrawFractal;
  double [] fParameters = new double [4];

  // Constants for message passing.
  public static final int GET_START_VALUE = 1;



  /**
    * Create the panel for displaying the fractal plot.
    * Pass the x and y plot limits.
   **/
  public FractalPanel (FractalController controller,
                       DrawFunction drawFractal,
                       double x_data_min, double x_data_max,
                       double y_data_min, double y_data_max) {

    fController = controller;
    setRange (x_data_min, x_data_max, y_data_min, y_data_max);
    fDrawFractal = drawFractal;

  } // ctor


  /** Set the x and y data range values. Rescale accordingly. **/
  public void setRange (double x_data_min, double x_data_max,
                        double y_data_min, double y_data_max) {

    fXDataMin = x_data_min;
    fXDataMax = x_data_max;
    fYDataMin = y_data_min;
    fYDataMax = y_data_max;

    getScaling ();

    // Turn off controller during fractal generation
    fController.setBusy (true);

    // Repaint the plot with the new dimensions.
    repaint ();

  } // setRange


  /** Pass a new alogorthim to plot. **/
  public void setAlgorithm (DrawFunction drawFractal) {
    fDrawFractal= drawFractal;
  } // setAlgorithm


  /**
    * Get the status info on the mouse and
    * send the info to the controller.
   **/
  public void handleMouseInfo (int flag) {
    if (flag == RECT_DRAGGING) {
      //fController.sendInfo (flag);
    } else
    if (flag == RECT_DONE) {

      // Get the rectangle coords in pixel dimensions
      int rect_x_pix_beg = Math.min (fXMouseStart, fXMouseEnd)
                           - fFrameX;
      int rect_x_pix_end = Math.max (fXMouseStart, fXMouseEnd)
                           - fFrameX;

      int rect_y_pix_beg = Math.min (fYMouseStart, fYMouseEnd)
                           - fFrameY;
      int rect_y_pix_end = Math.max (fYMouseStart, fYMouseEnd)
                           - fFrameY;

      // Check limits in horizontal
      if (rect_x_pix_beg < 0) rect_x_pix_beg = 0;
      if (rect_x_pix_end > fFrameWidth)
            rect_x_pix_end = fFrameWidth;
      // and in vertical.
      if (rect_y_pix_beg < 0) rect_y_pix_beg = 0;
      if (rect_y_pix_end > fFrameHeight)
            rect_y_pix_end = fFrameHeight;

      // Now convert from pixel scale to data scale
      // First calculate conversion factor from pixels
      // to data for the x axis.
      double range = fXDataMax - fXDataMin;
      double del = range/fFrameWidth;

      // Then apply it to the horizontal poitns.
      fXDataMin = rect_x_pix_beg * del + fXScaleValue[0];
      fXDataMax = rect_x_pix_end * del + fXScaleValue[0];

      // Calculate conversion factor for vertical axis.
      range = fYDataMax - fYDataMin;
      del = range/fFrameHeight;

      // and then apply to vertical points.
      fYDataMin = fYScaleValue[0] + (range - rect_y_pix_end * del);
      fYDataMax = fYScaleValue[0] + (range - rect_y_pix_beg * del);

      // Re-set the axes settings according to the new rectangle.
      getScaling ();

      // Tell the controller about the new settings
      fController.setFractalParameters (fXDataMin, fXDataMax,
                                        fYDataMin, fYDataMax);

      // Turn off controller during fractal generation
      fController.setBusy (true);

      repaint ();
    };
  } // handleMouseInfo


  /**
    * Get the values for the scaling numbers on
    * the plot axes.
   **/
  void getScaling () {

    fYScaleValue = new double[fNumYScaleValues];
    // Use lowest value of 0;
    fYScaleValue[0] = fYDataMin;
    fYScaleValue[fNumYScaleValues-1] = fYDataMax;

    // Then calculate the difference between the values
    double range = fYDataMax - fYDataMin;
    double del = range/(fNumYScaleValues-1);

    // Now set the intermediate scale values.
    for (int i=1; i < (fNumYScaleValues-1); i++) {
        fYScaleValue[i] = i * del + fYScaleValue[0];
    }


    fXScaleValue = new double[fNumXScaleValues];
    // First get the low and high values;
    fXScaleValue[0] = fXDataMin;
    fXScaleValue[fNumXScaleValues-1] = fXDataMax;

    // Then calculate the difference between the values
    range = fXDataMax - fXDataMin;
    del = range/(fNumXScaleValues-1);

    // Now set the intermediate scale values.
    for (int i=1; i < (fNumXScaleValues-1); i++) {
        fXScaleValue[i] = i * del + fXScaleValue[0];
    }
  } // getScaling



  /**
    * Override paintContents() to draw the plot inside the frame.
    * From the data arrays draw a different symbol for each data
    * data point and also draw x, y error bars.
   **/
  void paintContents (Graphics g) {

    g.setColor (Color.BLUE);

    // Draw the numbers along the axes
    drawAxesNumbers ( g, fXScaleValue, fYScaleValue);

    // Pass the x and y data ranges to the algorithm.
    fParameters[0] = fXDataMax - fXDataMin;
    fParameters[1] = fYDataMax - fYDataMin;
    fController.getFractalParameters (fParameters);

    fDrawFractal.setParameters (fParameters, null);
    fDrawFractal.setColor (Color.LIGHT_GRAY);


    // Draw the bifurcation plot
    fDrawFractal.draw (g,
                       fFrameX,     fFrameY,
                       fFrameWidth, fFrameHeight,
                       fXScaleValue,fYScaleValue);

    fController.setBusy (false);

  } // paintContents


  /**
    * Set the title of the plot.
   **/
  void setTitle (String title)
  { fTitle = title;}


  /**
    * Return the title at the top of the plot.
    * Overrides method in PlotPanel.
   **/
  String getTitle ()
  {  return fTitle;}


  /**
    * Return the label on the horizontal axis.
    * Overrides method in PlotPanel.
   **/
  String getXLabel ()
  {  return "Arbitrary units";}

} // class FractalPanel

  

 

 

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.