Home : Course Map : Chapter 6 : Java : Tech :
Drawing Panel - Polymorphism in action
JavaTech
Course Map
Chapter 6

Introduction
AWT
Swing
Containers
  Demo 1
UI Components
  Demo 2
UI Layout
  Demo 3   Demo 4
Text Display
  Demo 5
Drawing
  Demo 6   Demo 7
Draw Polygons
  Demo 8   Demo 9
Colors
Text Draw 
  Demo 10
Images
  Demo 11
Exercises

    Supplements
AWT
  Demo 1
Drawing
  Demo 2
Text Drawing
  Demo 3
UI Components
  Demo 4

Java2D
Shapes & Areas
  Demo 1   Demo 2
Stroke & Paint
  Demo 3
Transforms
  Demo 4
Gradients&Textures
  Demo 5   Demo 6
Text
  Demo 7   Demo 8
     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

In the previous "Starting to Plot" section, we introduced the PlotPanel abstract class, which we plan to use as the base class for various graphics tasks that require scaled axes. For example, in the previous demonstration program we created the DemoPanel subclass of PlotPanel to show how to draw points with error bars. For other types of displays, such as ploting a curved line through data points, we can create other PlotPanel subclasses for each case.

Here we will introduce a PlotPanel subclass that will greatly simplify our drawing code by employing the polymorphic techniques discussed in Chatper 4.

DrawFunction is an abstract class that holds a draw() method. Subclasses of DrawFunction override this method to create particular types of drawings such as lines or points. The setParameters() method in DrawFunction allows for setting the particular attributes of the line, points, etc. Its first argument is a 1D array and the second is a 2D array. The subclass can use these as is convenient.

DrawPanel receives an array of DrawFunction objects. In DrawPanel the paintContents() method loops through the array and invokes the draw() method for each DrawFunction object. Each of the DrawFunction objects overlays its particular drawing. This allows for the possibility of building complex composite drawings with great flexibility.

This approach also provides a good illustration of polymorphism, as discussed in Chapter 4: Java. The draw() method of the DrawFunction base class is invoked but the method actually executed is that of the overriding subclass. The two DrawFunction subclasses used in the demo program below are:

  • DrawLine is a subclass of DrawFunction that draws a straight line on the panel. The intercept and slope of the line are passed to it via the 1D array argument in the setParameters() method.

  • DrawPoints is a subclass of DrawFunction that draws a set of points and allows for optional x and y error bars. The arrays for the coordinates and the errors are passed via the 2D array in the second argument of the setParameters() method.

Note that the of DrawFunction subclass drawing settings are in the scale units (km, kg, or whatever) of the data given to the DrawPanel. The paintContents() method will determine the conversion to pixel coordinates. So when you create a particular DrawFunction object, you only have to deal with the data's own scale units and not worry about the display scaling.

PlotDemoApplet below demonstrates this approach. It first creates an arbitrary line and then some points along the line


PlotDrawApplet.java - demonstrates DrawPanel. The DrawPanel object uses a DrawPoints and a DrawLine object to draw a set of points and a line through them.

+ New classes:
DrawPanel.java
- this PlotPanel subclass invokes the draw(,,) methods of an array of DrawFunction subclass objects. Different types of DrawFunction subclasses

DrawPoints.java - subclass of DrawFunction for drawing points on DrawPanel. Provides the option drawing error bars. Arrays for x and y coordinates along with arrays for the x and y errors are passed via a 2-D array in setParameters(,,).

DrawLine.java - subclass of DrawFunction that draws a straight line on DrawPanel. Provides the option drawing error bars. The slope and intercept (for y = intercept + slope*x ) are passed via setParameters(,,).

DrawFunction.java - abstract class with a draw(,,) and a setParameters(,,) method. The DrawPanel drawContents(,,) method loops through an array of DrawFunction objects to invoke their draw(,,) method and thus overlay an assortment of drawn points, lines, etc

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

Java Notes

  • Illustrates polymorphic techniques with the use of the DrawFunction array in DrawPanel. Though the array is of the base class type DrawFunction, invoking its draw(,,) method will actually invoke the overriding draw(,,) method in the object's subclass.

/**
  *  Demonstrate the DrawPanel, which invokes the draw methods
  *  of a list of DrawFunction objects. This illustrates how to
  *  separate the display surface from the drawings in a modular
  *  approach.
 **/
public class PlotDrawApplet extends JApplet
{
  public void init () {
    Container content_pane = getContentPane ();

    // Create a line and some points along the line
    double y_data_min = 0.0;
    double y_data_max = 100.0;
    double x_data_min = 0.0;
    double x_data_max = 500.0;

    double y_intercept = 10.0;
    double slope      = 0.16;

    double [] line_parameters = new double[2];
    line_parameters[0] = y_intercept;
    line_parameters[1] = slope;

    // Create the object to draw a straight line.
    DrawLine draw_line = new DrawLine ();

    draw_line.setParameters (line_parameters, null);

    // Create the points data
    double [] y = new double[10];
    double [] x = new double[10];
    double [] y_err = new double[10];

    // Need a 2 D array to pass to DrawPoints object.
    double [][] data = new double[4][];
    data[0] = y;
    data[1] = x;
    data[2] = y_err;
    data[3] = null;// no error on x coordinate

    // Create some data points using the line.
    double x_pos = 45.0;
    double err  =  4.0;

    for (int i=0; i < 10; i++){
        y[i] = y_intercept + slope * x_pos;
        x[i] = x_pos;
        y_err[i] = err;
        err *= 1.20; // let error increase with x
        x_pos += 45;
    }

    // Create a DrawPoints instance to draw the points on
    // the DrawPanel.
    DrawPoints draw_points = new DrawPoints ();
    draw_points.setParameters (null, data);

    DrawFunction  [] drawFunctions = new DrawFunction[2];

    drawFunctions[0] = draw_line;
    drawFunctions[1] = draw_points;

    // Create an instance of DrawPanel with the range limits and
    // the functions to draw.
    DrawPanel draw_panel = new DrawPanel (y_data_min, y_data_max,
                                          x_data_min,  x_data_max,
                                          drawFunctions);

    // And add one or more panels to the JApplet panel.
    content_pane.add (draw_panel);
  } // init

} // class PlotDrawApplet



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

/**
  *  Draws a set of functions within the plotting area.
  *  Instances of subclasses of DrawFunction each draw
  *  their respective function on the panel.
 **/
public class DrawPanel extends PlotPanel
{
  String fTitle = "Draw Functions";
  String fXLabel= "Y vs X";

  // Array of functions to draw.
  DrawFunction [] fDrawFunctions;

  // These numbers determine the axes ranges.
  double fYDataMin = 0.0;
  double fYDataMax = 1.0;
  double fXDataMin = 0.0;
  double fXDataMax = 1.0;

  // Arrays to use for plotting axes scale values
  double [] fXScaleValue;
  double [] fYScaleValue;

  // Number of values for the x and y axes scales.

  int fNumYScaleValues = 2;
  int fNumXScaleValues = 5;

  /**
    *  Set the upper and lower limits for the drawing area.
    *  Assumes the number of elements in the arrays are
    *  the same.
   **/
  public DrawPanel (double y_data_min, double y_data_max,
                    double x_data_min, double x_data_max,
                    DrawFunction [] draw_functions) {
    fDrawFunctions = draw_functions;

    fYDataMax = y_data_max;
    fXDataMax = x_data_max;

    fYDataMin = y_data_min;
    fXDataMin = x_data_min;

    getScaling ();

  } // ctor

  /**
    * 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 = fYScaleValue[fNumYScaleValues-1]
                        - fYScaleValue[0];
    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 = fXScaleValue[fNumXScaleValues-1]
                        - fXScaleValue[0];
    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

  /**
    *  Invoke the draw method for the list of
    *  DrawFunction instances.
    *  Note that the frame area is clipped so further use of
    *  g will also be clipped to show only drawing within the
    *  scaled frame area.
   **/
  void paintContents (Graphics g) {
    if (fDrawFunctions == null) return;

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

    // Keep drawing within the frame box
    g.setClip (fFrameX, fFrameY, fFrameWidth, fFrameHeight);

    // Now loop through all the
    for (int i=0; i < fDrawFunctions.length; i++) {
         fDrawFunctions[i].draw (
                          g,
                          fFrameX,     fFrameY,
                          fFrameWidth, fFrameHeight,
                          fXScaleValue,fYScaleValue);
    }
  } // paintContents

  /** Return the plot title. **/
  String getTitle ()
  {  return fTitle;}


  /** Return the x axis label.  **/
  String getXLabel ()
  {  return fXLabel;}


  // Additional methods for setting the title and label

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


  /** Set x axis label. **/
  void setXLabel (String x_label)
  {  fXLabel = x_label;}


  /** Set scaling limits for axes.  **/
  void setScaleLimits (double y_data_min, double y_data_max,
                       double x_data_min, double x_data_max) {
    this.fYDataMax = y_data_max;
    this.fXDataMax = x_data_max;

    this.fYDataMin = y_data_min;
    this.fXDataMin = x_data_min;

    getScaling ();
  }


  /**  Pass a new array of DrawFunction objects. **/
  void setDrawFunctions (DrawFunction [] draw_functions){
    fDrawFunctions = draw_functions;
  }

} // class DrawPanel


/**
  *  Abstract base class for drawing functions onto
  *  the PlotPanel subclasses.
 **/
public abstract class DrawFunction
{
  double [] fParameters;
  double [][] fData;
  Color fColor = Color.BLACK;

  /**
    *  Abstract base class for drawing functions onto
    *  the PlotPanel subclasses.
    *
    *  @param g graphics context
    *  @param frameWidth display area width in pixels.
    *  @param frameHeight display area height in pixels.
    *  @param frameStartX horizontal point on display where
    *  drawing starts in pixel number.
    *  @param frameStartY vertical point on display where
    *  drawing starts in pixel number.
    *  @param xScale 2 dimensional array holding lower and
    *  upper values of the function input scale range.
    *  @param yScale 2 dimensional array holding lower and
    *  upper values of the function output scale range.
    */
  public abstract void draw (Graphics g,
                   int frame_start_x, int frame_start_y,
                   int frame_width, int frame_height,
                   double [] x_scale, double [] y_scale);

  /**
    *  Parameters and data for the function.
   **/
  public void setParameters (double [] parameters,
                             double [][] data) {
     fParameters = parameters;
     fData = data;
  }

  public void setColor (Color color){
    fColor = color;
  }
}
// class DrawFunction


import java.awt.*;

/** Drawing straight line onto the PlotPanel. **/
public class DrawLine extends DrawFunction
{
  /**
    *  Draw a straight line 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 ();

    g.setColor (fColor);

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

    // Get the number of horizontal scale values.
    int num_x_points = x_scale.length;
    int num_y_points = y_scale.length;


    // Get conversion factor from data scale to frame pixels
    double y_scaleFactor =
      frame_height/(y_scale[num_y_points-1] - y_scale[0]);

    // Get the vertical coord vs the first and last points on
    // the horizontal axis.
    double y0 = fParameters[0] + fParameters[1] * x_scale[0];
    double y1 = fParameters[0] + fParameters[1] * x_scale[num_x_points-1];

    // Convert to pixel nunber
    int y_0_frame = frame_height -  (int)(y0 * y_scaleFactor) +
                      frame_start_y;
    int y_1_frame = frame_height -  (int)(y1 * y_scaleFactor) +
                      frame_start_y;

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

    // Draw the straight line.
    g.drawLine (frame_start_x, y_0_frame,
                frame_start_x + frame_width, y_1_frame);

    g.setColor (save_color);

  } // draw

} // class DrawLine


import java.awt.*;

/**
  *  Drawing points on the PlotPanel. Allow for the option
  *  of different symbols as well as error bars.
 **/
public class DrawPoints extends DrawFunction
{
  double [] fXData;
  double [] fYData;

  double [] fXErr;
  double [] fYErr;

  // Symbol types for plotting
  public final static int RECT     = 0;
  public final static int RECTFILL = 1;
  public final static int OVAL     = 2;
  public final static int OVALFILL = 3;
  int symbolType = 3;// default filled oval

  Color fSymbolColor = Color.BLUE;
  Color fErrorColor  = Color.RED;

  /**
    *  Draw a straight line  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){

    // Check if ready to draw the line
    if (fXData == null || fYData == null) return;

    Color saveColor = g.getColor ();

    // Get the number of horizontal scale values.
    int num_x_points = x_scale.length;
    int num_y_points = y_scale.length;

    // Get conversion factor from data scale to frame pixels
    double y_scaleFactor =
      frame_height/(y_scale[num_y_points-1] - y_scale[0]);
    double x_scaleFactor =
      frame_width/(x_scale[num_x_points-1] - x_scale[0]);

    // Plot points
    for (int i=0; i < fXData.length; i++) {
        // Convert to pixel nunber
        int y = frame_height - (int)((fYData[i] -
                  y_scale[0]) * y_scaleFactor)
                      + frame_start_y;
        int x = (int)((fXData[i] - x_scale[0]) * x_scaleFactor) +
                  frame_start_x;

        // For data symbols get size relative to the frame.
        int symDim =  (int)(frame_width *.01);

        // Draw data point symbols
        g.setColor (fSymbolColor);

        // Now draw the desired symbol.
        switch ( symbolType) {
          case RECT :
            g.drawRect (x-symDim,y-symDim, 2*symDim, 2*symDim);
            break;

          case RECTFILL :
            g.fillRect (x-symDim,y-symDim, 2*symDim+1, 2*symDim+1);
            break;

          case OVAL :
            g.drawOval (x-symDim,y-symDim, 2*symDim, 2*symDim);
            break;

          case OVALFILL :
          default :
            g.fillOval (x-symDim,y-symDim, 2*symDim+1, 2*symDim+1);
            break;
        }

        g.setColor (fErrorColor);


        // Use error array references as flags for drawing errors
        if ( fXErr != null) {
            int xBar =  (int) (fXErr[i]*x_scaleFactor/2.0);
            // Draw x error bar
            g.drawLine (x-xBar,y,x+xBar,y);
        }
        if ( fYErr != null) {
            int yBar =  (int) (fYErr[i]*y_scaleFactor/2.0);
            // Draw y error bar
            g.drawLine (x,y-yBar,x,y+yBar);
        }
    }
    g.setColor (saveColor);

  } // draw

  /**  Pass coords of the points to draw. **/
  public void setParameters (double [] param, double [][] data ){
    fYData = data[0];
    fXData = data[1];
    fYErr  = data[2];
    fXErr  = data[3];
  }

  /** Set the type of symbol to use for the points. **/
  public void setSymbolType (int type){
    symbolType = type;
  }

} // class DrawPoints


 

References & Web Resources

Last Update: Oct. 30, 2004

            Tech
Java Tech Graphics
Starting to Plot
  Demo 1
Drawing Panel
  Demo 2
Histogram Display

  Demo 3
Exercises

           Physics
Display Text Data
  Demo 1
Plot Data
  Demo 2
Find Max/Min
  Demo 3
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.