| 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 |