gt:             new Color(0x0000ff), // blue
context: package fr.inria.contraintes.biocham.customComponents;
  
  
  import fr.inria.contraintes.biocham.customComponents.Plot.Format;
  import fr.inria.contraintes.biocham.utils.EPSGraphics;
  
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.EventQueue;
  import java.awt.FlowLayout;
  import java.awt.Font;
  import java.awt.FontMetrics;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.Rectangle;
  import java.awt.RenderingHints;
  import java.awt.event.InputEvent;
  import java.awt.event.KeyEvent;
  import java.awt.event.KeyListener;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  import java.awt.event.MouseMotionListener;
  import java.awt.image.BufferedImage;
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.io.Writer;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Enumeration;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Vector;
  
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.SwingUtilities;
  
  
  /**
   * Class that creates a custom JPanel component that can hold a Plot image.
   *  
   */ 
  public class PlotBox extends JPanel {
      ///////////////////////////////////////////////////////////////////
      ////                         constructor                       ////
  
  	Vector<Format> _formats;
      /** Construct a plot box with a default configuration. */
      public PlotBox() {
          // If we make this transparent, the background shows through.
          // However, we assume that the user will set the background.
          // NOTE: A component is transparent by default (?).
          // setOpaque(false);
          setOpaque(true);
          _formats=new Vector<Format>();
          // Create a right-justified layout with spacing of 2 pixels.
          setLayout(new FlowLayout(FlowLayout.RIGHT, 2, 2));
          addMouseListener(new ZoomListener());
          addKeyListener(new CommandListener());
          addMouseMotionListener(new DragListener());
  
          // This is something we want to do only once...
          _measureFonts();
  
          // Request the focus so that key events are heard.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
      ///////////////////////////////////////////////////////////////////
      ////                         public methods                    ////
  
      /** Add a legend (displayed at the upper right) for the specified
       *  data set with the specified string.  Short strings generally
       *  fit better than long strings.  If the string is empty, or the
       *  argument is null, then no legend is added.
       *  @param dataset The dataset index.
       *  @param legend The label for the dataset.
       *  @see #renameLegend(int, String)
       */
      public synchronized void addLegend(int dataset, String legend) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if ((legend == null) || legend.length()<1) {
              return;
          }
  
          _legendStrings.addElement(legend);
          _legendDatasets.addElement(new Integer(dataset));
      }
  
      /** Specify a tick mark for the X axis.  The label given is placed
       *  on the axis at the position given by <i>position</i>. If this
       *  is called once or more, automatic generation of tick marks is
       *  disabled.  The tick mark will appear only if it is within the X
       *  range.
       *  @param label The label for the tick mark.
       *  @param position The position on the X axis.
       */
      public synchronized void addXTick(String label, double position) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (_xticks == null) {
              _xticks = new Vector();
              _xticklabels = new Vector();
          }
  
          _xticks.addElement(new Double(position));
          _xticklabels.addElement(label);
      }
  
      /** Specify a tick mark for the Y axis.  The label given is placed
       *  on the axis at the position given by <i>position</i>. If this
       *  is called once or more, automatic generation of tick marks is
       *  disabled.  The tick mark will appear only if it is within the Y
       *  range.
       *  @param label The label for the tick mark.
       *  @param position The position on the Y axis.
       */
      public synchronized void addYTick(String label, double position) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          if (_yticks == null) {
              _yticks = new Vector();
              _yticklabels = new Vector();
          }
  
          _yticks.addElement(new Double(position));
          _yticklabels.addElement(label);
      }
  
      /** If the argument is true, clear the axes.  I.e., set all parameters
       *  controlling the axes to their initial conditions.
       *  For the change to take effect, call repaint().  If the argument
       *  is false, do nothing.
       *  @param axes If true, clear the axes parameters.
       */
      public synchronized void clear(boolean axes) {
          // We need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xBottom = Double.MAX_VALUE;
          _xTop = -Double.MAX_VALUE;
          _yBottom = Double.MAX_VALUE;
          _yTop = -Double.MAX_VALUE;
  
          if (axes) {
              // Protected members first.
              _yMax = 0;
              _yMin = 0;
              _xMax = 0;
              _xMin = 0;
              _xRangeGiven = false;
              _yRangeGiven = false;
              _originalXRangeGiven = false;
              _originalYRangeGiven = false;
              _rangesGivenByZooming = false;
              _xlog = false;
              _ylog = false;
              _grid = true;
              _wrap = false;
              _usecolor = true;
  
              // Private members next...
              _filespec = null;
              _xlabel = null;
              _ylabel = null;
              _title = null;
              _legendStrings = new Vector();
              _legendDatasets = new Vector();
              _xticks = null;
              _xticklabels = null;
              _yticks = null;
              _yticklabels = null;
          }
      }
  
      /** Clear all legends.  This will show up on the next redraw.
       */
      public synchronized void clearLegends() {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
          boolean b1=SwingUtilities.isEventDispatchThread();
          _legendStrings = new Vector();
          _legendDatasets = new Vector();
          repaint();
      }
  
      /** If this method is called in the event thread, then simply
       * execute the specified action.  Otherwise,
       * if there are already deferred actions, then add the specified
       * one to the list.  Otherwise, create a list of deferred actions,
       * if necessary, and request that the list be processed in the
       * event dispatch thread.
       *
       * Note that it does not work nearly as well to simply schedule
       * the action yourself on the event thread because if there are a
       * large number of actions, then the event thread will not be able
       * to keep up.  By grouping these actions, we avoid this problem.
       *
       * This method is not synchronized, so the caller should be.
       * @param action The Runnable object to execute.
       */
      public void deferIfNecessary(Runnable action) {
          // In swing, updates to showing graphics must be done in the
          // event thread.  If we are in the event thread, then proceed.
          // Otherwise, queue a request or add to a pending request.
          if (EventQueue.isDispatchThread()) {
              action.run();
          } else {
              if (_deferredActions == null) {
                  _deferredActions = new LinkedList();
              }
  
              // Add the specified action to the list of actions to perform.
              _deferredActions.add(action);
  
              // If it hasn't already been requested, request that actions
              // be performed in the event dispatch thread.
              if (!_actionsDeferred) {
                  Runnable doActions = new Runnable() {
                      public void run() {
                          _executeDeferredActions();
                      }
                  };
  
                  try {
                      // NOTE: Using invokeAndWait() here risks causing
                      // deadlock.  Don't do it!
                      SwingUtilities.invokeLater(doActions);
                  } catch (Exception ex) {
                      // Ignore InterruptedException.
                      // Other exceptions should not occur.
                  }
  
                  _actionsDeferred = true;
              }
          }
      }
  
      /** Export a description of the plot.
       *  Currently, only EPS is supported.  But in the future, this
       *  may cause a dialog box to open to allow the user to select
       *  a format.  If the argument is null, then the description goes
       *  to the clipboard.  Otherwise, it goes to the specified file.
       *  To send it to standard output, use
       *  <code>System.out</code> as an argument.
       *  @param out An output stream to which to send the description.
       */
      public synchronized void export(OutputStream out) {
          try {
              EPSGraphics g = new EPSGraphics(out, _width, _height);
              _drawPlot(g, false);
              g.showpage();
          } catch (RuntimeException ex) {
              String message = "Export failed: " + ex.getMessage();
              JOptionPane.showMessageDialog(this, message,
                      "Ptolemy Plot Message", JOptionPane.ERROR_MESSAGE);
  
              // Rethrow the exception so that we don't report success,
              // and so the stack trace is displayed on standard out.
              throw (RuntimeException) ex.fillInStackTrace();
          }
      }
  
      // CONTRIBUTED CODE.
      // I wanted the ability to use the Plot object in a servlet and to
      // write out the resultant images. The following routines,
      // particularly exportImage(), permit this. I also had to make some
      // minor changes elsewhere. Rob Kroeger, May 2001.
      // NOTE: This code has been modified by EAL to conform with Ptolemy II
      // coding style.
  
      /** Create a BufferedImage and draw this plot to it.
       *  The size of the returned image matches the current size of the plot.
       *  This method can be used, for
       *  example, by a servlet to produce an image, rather than
       *  requiring an applet to instantiate a PlotBox.
       *  @return An image filled by the plot.
       */
      public synchronized BufferedImage exportImage(Vector<Format> formats) {
      	boolean b1=SwingUtilities.isEventDispatchThread();
      	_formats=formats;
      	int siz=_formats.size();
          Rectangle rectangle = new Rectangle(_preferredWidth, _preferredHeight);
          return exportImage(new BufferedImage(rectangle.width, rectangle.height,
                  BufferedImage.TYPE_INT_ARGB), rectangle,
                  _defaultImageRenderingHints(), false);
      }
  
      /** Create a BufferedImage the size of the given rectangle and draw
       *  this plot to it at the position specified by the rectangle.
       *  The plot is rendered using anti-aliasing.
       *  @param rectangle The size of the plot. This method can be used, for
       *  example, by a servlet to produce an image, rather than
       *  requiring an applet to instantiate a PlotBox.
       *  @return An image containing the plot.
       */
      public synchronized BufferedImage exportImage(Rectangle rectangle) {
          return exportImage(new BufferedImage(rectangle.width, rectangle.height,
                  BufferedImage.TYPE_INT_ARGB), rectangle,
                  _defaultImageRenderingHints(), false);
      }
      
      /**
       * My method, combination of two: formats+size as an input!
       * @author Dragana Jovanovska
       */
      public synchronized BufferedImage exportImage(Vector<Format> formats,Rectangle rectangle) {
      	_formats=formats;
      	return exportImage(new BufferedImage(rectangle.width, rectangle.height,
                  BufferedImage.TYPE_INT_ARGB), rectangle,
                  _defaultImageRenderingHints(), false);
      }
  
      /** Draw this plot onto the specified image at the position of the
       *  specified rectangle with the size of the specified rectangle.
       *  The plot is rendered using anti-aliasing.
       *  This can be used to paint a number of different
       *  plots onto a single buffered image.  This method can be used, for
       *  example, by a servlet to produce an image, rather than
       *  requiring an applet to instantiate a PlotBox.
       *  @param bufferedImage Image onto which the plot is drawn.
       *  @param rectangle The size and position of the plot in the image.
       *  @param hints Rendering hints for this plot.
       *  @param transparent Indicator that the background of the plot
       *   should not be painted.
       *  @return The modified bufferedImage.
       */
      public synchronized BufferedImage exportImage(BufferedImage bufferedImage,
              Rectangle rectangle, RenderingHints hints, boolean transparent) {
          Graphics2D graphics = bufferedImage.createGraphics();
          graphics.addRenderingHints(_defaultImageRenderingHints());
  
          if (!transparent) {
              graphics.setColor(Color.white); // set the background color
              graphics.fill(rectangle);
          }
  
          _drawPlot(graphics, false, rectangle,_formats);
          return bufferedImage;
      }
  
      /**        Draw this plot onto the provided image.
       *  This method does not paint the background, so the plot is
       *  transparent.  The plot fills the image, and is rendered
       *  using anti-aliasing.  This method can be used to overlay
       *  multiple plots on the same image, although you must use care
       *  to ensure that the axes and other labels are identical.
       *  Hence, it is usually better to simply combine data sets into
       *  a single plot.
       *  @param bufferedImage The image onto which to render the plot.
       *  @return The modified bufferedImage.
       */
      public synchronized BufferedImage exportImage(BufferedImage bufferedImage) {
          return exportImage(bufferedImage, new Rectangle(bufferedImage
                  .getWidth(), bufferedImage.getHeight()),
                  _defaultImageRenderingHints(), true);
      }
  
      /** Rescale so that the data that is currently plotted just fits.
       *  This is done based on the protected variables _xBottom, _xTop,
       *  _yBottom, and _yTop.  It is up to derived classes to ensure that
       *  variables are valid.
       *  This method calls repaint(), which eventually causes the display
       *  to be updated.
       */
      public synchronized void fillPlot() {
          // NOTE: These used to be _setXRange() and _setYRange() to avoid
          // confusing this with user-specified ranges.  But we want to treat
          // a fill command as a user specified range.
          // EAL, 6/12/00.
          setXRange(_xBottom, _xTop);
          setYRange(_yBottom, _yTop);
          repaint();
  
          // Reacquire the focus so that key bindings work.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
      /** Return whether the plot uses color.
       *  @return True if the plot uses color.
       */
      public boolean getColor() {
          return _usecolor;
      }
  
      /** Get the point colors.
       *  @return Array of colors
       *  @see #setColors(Color[])
       */
      public ArrayList<Color> getColors() {
          return _colors;
      }
  
      /** Convert a color name into a Color. Currently, only a very limited
       *  set of color names is supported: black, white, red, green, and blue.
       *  @param name A color name, or null if not found.
       *  @return An instance of Color.
       */
      public static Color getColorByName(String name) {
          try {
              // Check to see if it is a hexadecimal
              if (name.startsWith("#")) {
                  name = name.substring(1);
              }
  
              Color col = new Color(Integer.parseInt(name, 16));
              return col;
          } catch (NumberFormatException e) {
          }
  
          // FIXME: This is a poor excuse for a list of colors and values.
          // We should use a hash table here.
          // Note that Color decode() wants the values to start with 0x.
          String[][] names = { { "black", "00000" }, { "white", "ffffff" },
                  { "red", "ff0000" }, { "green", "00ff00" },
                  { "blue", "0000ff" } };
  
          for (int i = 0; i < names.length; i++) {
              if (name.equals(names[i][0])) {
                  try {
                      Color col = new Color(Integer.parseInt(names[i][1], 16));
                      return col;
                  } catch (NumberFormatException e) {
                  	e.printStackTrace();
                  }
              }
          }
  
          return null;
      }
  
  
      /** Return whether the grid is drawn.
       *  @return True if a grid is drawn.
       */
      public boolean getGrid() {
          return _grid;
      }
  
      /** Get the legend for a dataset, or null if there is none.
       *  The legend would have been set by addLegend().
       *  @param dataset The dataset index.
       *  @return The legend label, or null if there is none.
       */
      public synchronized String getLegend(int dataset) {
          int idx = _legendDatasets.indexOf(new Integer(dataset), 0);
  
          if (idx != -1) {
              return (String) _legendStrings.elementAt(idx);
          } else {
              return null;
          }
      }
  
      /** Given a legend string, return the corresponding dataset or -1 if no
       *  legend was added with that legend string
       *  The legend would have been set by addLegend().
       *  @param legend The String naming the legend
       *  @return The legend dataset, or -1 if not found.
       *  @since Ptplot 5.2p1
       */
      public synchronized int getLegendDataset(String legend) {
          int index = _legendStrings.indexOf(legend);
  
          if (index == -1) {
              return -1;
          }
  
          return ((Integer) _legendDatasets.get(index)).intValue();
      }
  
      /** If the size of the plot has been set by setSize(),
       *  then return that size.  Otherwise, return what the superclass
       *  returns (which is undocumented, but apparently imposes no maximum size).
       *  Currently (JDK 1.3), only BoxLayout pays any attention to this.
       *  @return The maximum desired size.
       */
  
      //     public synchronized Dimension getMaximumSize() {
      //         if (_sizeHasBeenSet) {
      //             return new Dimension(_preferredWidth, _preferredHeight);
      //         } else {
      //             return super.getMaximumSize();
      //         }
      //     }
      /** Get the minimum size of this component.
       *  This is simply the dimensions specified by setSize(),
       *  if this has been called.  Otherwise, return whatever the base
       *  class returns, which is undocumented.
       *  @return The minimum size.
       */
  
      //     public synchronized Dimension getMinimumSize() {
      //         if (_sizeHasBeenSet) {
      //             return new Dimension(_preferredWidth, _preferredHeight);
      //         } else {
      //             return super.getMinimumSize();
      //         }
      //     }
      /** Get the current plot rectangle.
       *  Note that Rectangle returned by this method is calculated
       *  from the values of {@link #_ulx}, {@link #_uly},
       *  {@link #_lrx} and {@link #_lry}.  The value passed in by
       *  setPlotRectangle() is not directly used, thus calling
       *  getPlotRectangle() may not return the same rectangle that
       *  was passed in with setPlotRectangle().
       *  @return Rectangle
       *  @see #setPlotRectangle(Rectangle)
       */
      public Rectangle getPlotRectangle() {
          return new Rectangle(_ulx, _uly, _lrx - _ulx, _lry - _uly);
      }
  
      /** Get the preferred size of this component.
       *  This is simply the dimensions specified by setSize(),
       *  if this has been called, or the default width and height
       *  otherwise (500 by 300).
       *  @return The preferred size.
       */
      public synchronized Dimension getPreferredSize() {
          return new Dimension(_preferredWidth, _preferredHeight);
      }
  
      /** Get the title of the graph, or an empty string if there is none.
       *  @return The title.
       */
      public synchronized String getTitle() {
          if (_title == null) {
              return "";
          }
  
          return _title;
      }
  
      /** Get the range for X values of the data points registered so far.
       *  Usually, derived classes handle managing the range by checking
       *  each new point against the current range.
       *  @return An array of two doubles where the first element is the
       *  minimum and the second element is the maximum.
       *  @see #getXRange()
       */
      public synchronized double[] getXAutoRange() {
          double[] result = new double[2];
          result[0] = _xBottom;
          result[1] = _xTop;
          return result;
      }
  
      /** Get the label for the X (horizontal) axis, or null if none has
       *  been set.
       *  @return The X label.
       */
      public synchronized String getXLabel() {
          return _xlabel;
      }
  
      /** Return whether the X axis is drawn with a logarithmic scale.
       *  @return True if the X axis is logarithmic.
       */
      public boolean getXLog() {
          return _xlog;
      }
  
      /** Get the X range. If {@link #setXRange(double, double)} has been
       *  called, then this method returns the values passed in as
       *  arguments to setXRange(double, double).  If setXRange(double,
       *  double) has not been called, then this method returns the
       *  range of the data to be plotted, which might not be all of the
       *  data due to zooming.
       *  @return An array of two doubles where the first element is the
       *  minimum and the second element is the maximum.
       *  @see #getXAutoRange()
       */
      public synchronized double[] getXRange() {
          double[] result = new double[2];
  
          if (_xRangeGiven) {
              result[0] = _xlowgiven;
              result[1] = _xhighgiven;
          } else {
              // Have to first correct for the padding.
              result[0] = _xMin + ((_xMax - _xMin) * _padding);
              result[1] = _xMax - ((_xMax - _xMin) * _padding);
              ;
          }
  
          return result;
      }
  
      /** Get the X ticks that have been specified, or null if none.
       *  The return value is an array with two vectors, the first of
       *  which specifies the X tick locations (as instances of Double),
       *  and the second of which specifies the corresponding labels.
       *  @return The X ticks.
       */
      public synchronized Vector[] getXTicks() {
          if (_xticks == null) {
              return null;
          }
  
          Vector[] result = new Vector[2];
          result[0] = _xticks;
          result[1] = _xticklabels;
          return result;
      }
  
      /** Get the range for Y values of the data points registered so far.
       *  Usually, derived classes handle managing the range by checking
       *  each new point against the range.
       *  @return An array of two doubles where the first element is the
       *  minimum and the second element is the maximum.
       *  @see #getYRange()
       */
      public synchronized double[] getYAutoRange() {
          double[] result = new double[2];
          result[0] = _yBottom;
          result[1] = _yTop;
          return result;
      }
  
      /** Get the label for the Y (vertical) axis, or null if none has
       *  been set.
       *  @return The Y label.
       */
      public String getYLabel() {
          return _ylabel;
      }
  
      /** Return whether the Y axis is drawn with a logarithmic scale.
       *  @return True if the Y axis is logarithmic.
       */
      public boolean getYLog() {
          return _ylog;
      }
  
      /** Get the Y range. If {@link #setYRange(double, double)} has been
       *  called, then this method returns the values passed in as
       *  arguments to setYRange(double, double).  If setYRange(double,
       *  double) has not been called, then this method returns the
       *  range of the data to be plotted, which might not be all of the
       *  data due to zooming.
       *  @return An array of two doubles where the first element is the
       *  minimum and the second element is the maximum.
       *  @see #getYAutoRange()
       */
      public synchronized double[] getYRange() {
          double[] result = new double[2];
  
          if (_yRangeGiven) {
              result[0] = _ylowgiven;
              result[1] = _yhighgiven;
          } else {
              // Have to first correct for the padding.
              result[0] = _yMin + ((_yMax - _yMin) * _padding);
              result[1] = _yMax - ((_yMax - _yMin) * _padding);
              ;
          }
  
          return result;
      }
  
      /** Get the Y ticks that have been specified, or null if none.
       *  The return value is an array with two vectors, the first of
       *  which specifies the Y tick locations (as instances of Double),
       *  and the second of which specifies the corresponding labels.
       *  @return The Y ticks.
       */
      public synchronized Vector[] getYTicks() {
          if (_yticks == null) {
              return null;
          }
  
          Vector[] result = new Vector[2];
          result[0] = _yticks;
          result[1] = _yticklabels;
          return result;
      }
  
      /** Initialize the component, creating the fill button and parsing
       *  an input file, if one has been specified.  This is deprecated.
       *  Call setButtons() and read() instead.
       *  @deprecated
       */
     /* public void init() {
          setButtons(true);
  
          if (_filespec != null) {
              parseFile(_filespec, _documentBase);
          }
      }
  */
      /** Paint the component contents, which in this base class is
       *  only the axes.
       *  @param graphics The graphics context.
       */
      public synchronized void paintComponent(Graphics graphics) {
          //  super.paintComponent(graphics);
          //         _drawPlot(graphics, true);
          BufferedImage newPlotImage = _plotImage;
  
          if (newPlotImage == null) {
              Rectangle bounds = getBounds();
              newPlotImage = new BufferedImage(bounds.width, bounds.height,
                      BufferedImage.TYPE_3BYTE_BGR);
              _plotImage = newPlotImage;
  
              Graphics2D offScreenGraphics = newPlotImage.createGraphics();
              super.paintComponent(offScreenGraphics);
              _drawPlot(offScreenGraphics, true);
              
          }
  
          // Blit the offscreen image onto the screen.
          graphics.drawImage(newPlotImage, 0, 0, null);
  
          // Acquire the focus so that key bindings work.
          // NOTE: no longer needed?
          // requestFocus();
      }
  
    
  
      /** Read commands and/or plot data from an input stream in the old
       *  (non-XML) file syntax.
       *  To update the display, call repaint(), or make the plot visible with
       *  setVisible(true).
       *  <p>
       *  To read from standard input, use:
       *  <pre>
       *     read(System.in);
       *  </pre>
       *  To read from a url, use:
       *  <pre>
       *     read(url.openStream());
       *  </pre>
       *  To read a URL from within an applet, use:
       *  <pre>
       *     URL url = new URL(getDocumentBase(), urlSpec);
       *     read(url.openStream());
       *  </pre>
       *  Within an application, if you have an absolute URL, use:
       *  <pre>
       *     URL url = new URL(urlSpec);
       *     read(url.openStream());
       *  </pre>
       *  To read from a file, use:
       *  <pre>
       *     read(new FileInputStream(filename));
       *  </pre>
       *  @param in The input stream.
       *  @exception IOException If the stream cannot be read.
       */
      public synchronized void read(InputStream in) throws IOException {
          try {
              // NOTE: I tried to use exclusively the jdk 1.1 Reader classes,
              // but they provide no support like DataInputStream, nor
              // support for URL accesses.  So I use the older classes
              // here in a strange mixture.
              BufferedReader din = new BufferedReader(new InputStreamReader(in));
  
              try {
                  String line = din.readLine();
  
                  while (line != null) {
                      _parseLine(line);
                      line = din.readLine();
                  }
              } finally {
                  din.close();
              }
          } catch (IOException e) {
              _errorMsg = new String[2];
              _errorMsg[0] = "Failure reading input data.";
              _errorMsg[1] = e.getMessage();
              throw e;
          }
      }
  
      /** Read a single line command provided as a string.
       *  The commands can be any of those in the ASCII file format.
       *  @param command A command.
       */
      public synchronized void read(String command) {
          _parseLine(command);
      }
  
      /** Remove the legend (displayed at the upper right) for the specified
       *  data set. If the dataset is not found, nothing will occur.
       *  The PlotBox must be repainted in order for this to take effect.
       *  @param dataset The dataset index.
       */
      public synchronized void removeLegend(int dataset) {
          final int len = _legendDatasets.size();
          int foundIndex = -1;
          boolean found = false;
  
          for (int i = 0; (i < len) && !found; ++i) {
              if (((Integer) _legendDatasets.get(i)).intValue() == dataset) {
                  foundIndex = i;
                  found = true;
              }
          }
  
          if (found) {
              _legendDatasets.remove(foundIndex);
              _legendStrings.remove(foundIndex);
          }
      }
  
      /** Rename a legend.
       *  @param dataset The dataset of the legend to be renamed.
       *  If there is no dataset with this value, then nothing happens.
       *  @param newName  The new name of legend.
       *  @see #addLegend(int, String)
       */
      public synchronized void renameLegend(int dataset, String newName) {
          int index = _legendDatasets.indexOf(new Integer(dataset), 0);
  
          if (index != -1) {
              _legendStrings.setElementAt(newName, index);
  
              // Changing legend means we need to repaint the offscreen buffer.
              _plotImage = null;
          }
      }
  
      /** Reset the X and Y axes to the ranges that were first specified
       *  using setXRange() and setYRange(). If these methods have not been
       *  called, then reset to the default ranges.
       *  This method calls repaint(), which eventually causes the display
       *  to be updated.
       */
      public synchronized void resetAxes() {
          setXRange(_originalXlow, _originalXhigh);
          setYRange(_originalYlow, _originalYhigh);
          repaint();
      }
  
      /** Do nothing in this base class. Derived classes might want to override
       *  this class to give an example of their use.
       */
      public void samplePlot() {
          // Empty default implementation.
      }
  
      /** Set the background color.
       *  @param background The background color.
       */
      public synchronized void setBackground(Color background) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _background = background;
          super.setBackground(_background);
      }
  
      /** Move and resize this component. The new location of the top-left
       *  corner is specified by x and y, and the new size is specified by
       *  width and height. This overrides the base class method to make
       *  a record of the new size.
       *  @param x The new x-coordinate of this component.
       *  @param y The new y-coordinate of this component.
       *  @param width The new width of this component.
       *  @param height The new height of this component.
       */
      public synchronized void setBounds(int x, int y, int width, int height) {
          _width = width;
          _height = height;
  
          // Resizing the component means we need to redraw the buffer.
          _plotImage = null;
  
          super.setBounds(x, y, _width, _height);
      }
  
     
      /** If the argument is false, draw the plot without using color
       *  (in black and white).  Otherwise, draw it in color (the default).
       *  @param useColor False to draw in back and white.
       */
      public synchronized void setColor(boolean useColor) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _usecolor = useColor;
      }
  
      /** Set the point colors.  Note that the default colors have been
       *  carefully selected to maximize readability and that it is easy
       *  to use colors that result in a very ugly plot.
       *  @param colors Array of colors to use in succession for data sets.
       *  @see #getColors()
       */
      public synchronized void setColors(ArrayList<Color> colors) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
          _colors = colors;
      }
     
  
      /** Set the foreground color.
       *  @param foreground The foreground color.
       */
      public synchronized void setForeground(Color foreground) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _foreground = foreground;
          super.setForeground(_foreground);
      }
  
      /** Control whether the grid is drawn.
       *  @param grid If true, a grid is drawn.
       */
      public synchronized void setGrid(boolean grid) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _grid = grid;
      }
  
      /** Set the label font, which is used for axis labels and legend labels.
       *  The font names understood are those understood by
       *  java.awt.Font.decode().
       *  @param name A font name.
       */
      public synchronized void setLabelFont(String name) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _labelFont = Font.decode(name);
          _labelFontMetrics = getFontMetrics(_labelFont);
      }
  
      /** Set the plot rectangle inside the axes.  This method
       *  can be used to create two plots that share the same axes.
       *  @param rectangle Rectangle space inside axes.
       *  @see #getPlotRectangle()
       */
      public synchronized void setPlotRectangle(Rectangle rectangle) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _specifiedPlotRectangle = rectangle;
      }
  
      /** Set the size of the plot.  This overrides the base class to make
       *  it work.  In particular, it records the specified size so that
       *  getMinimumSize() and getPreferredSize() return the specified value.
       *  However, it only works if the plot is placed in its own JPanel.
       *  This is because the JPanel asks the contained component for
       *  its preferred size before determining the size of the panel.
       *  If the plot is placed directly in the content pane of a JApplet,
       *  then, mysteriously, this method has no effect.
       *  @param width The width, in pixels.
       *  @param height The height, in pixels.
       */
      public synchronized void setSize(int width, int height) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _width = width;
          _height = height;
          _preferredWidth = width;
          _preferredHeight = height;
  
          //_sizeHasBeenSet = true;
          super.setSize(width, height);
      }
  
      /** Set the title of the graph.
       *  @param title The title.
       */
      public synchronized void setTitle(String title) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _title = title;
      }
  
      /** Set the title font.
       *  The font names understood are those understood by
       *  java.awt.Font.decode().
       *  @param name A font name.
       */
      public synchronized void setTitleFont(String name) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _titleFont = Font.decode(name);
          _titleFontMetrics = getFontMetrics(_titleFont);
      }
  
      /** Specify whether the X axis is wrapped.
       *  If it is, then X values that are out of range are remapped
       *  to be in range using modulo arithmetic. The X range is determined
       *  by the most recent call to setXRange() (or the most recent zoom).
       *  If the X range has not been set, then use the default X range,
       *  or if data has been plotted, then the current fill range.
       *  @param wrap If true, wrapping of the X axis is enabled.
       */
      public synchronized void setWrap(boolean wrap) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _wrap = wrap;
  
          if (!_xRangeGiven) {
              if (_xBottom > _xTop) {
                  // have nothing to go on.
                  setXRange(0, 0);
              } else {
                  setXRange(_xBottom, _xTop);
              }
          }
  
          _wrapLow = _xlowgiven;
          _wrapHigh = _xhighgiven;
      }
  
      /** Set the label for the X (horizontal) axis.
       *  @param label The label.
       */
      public synchronized void setXLabel(String label) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xlabel = label;
      }
  
      /** Specify whether the X axis is drawn with a logarithmic scale.
       *  If you would like to have the X axis drawn with a
       *  logarithmic axis, then setXLog(true) should be called before
       *  adding any data points.
       *  @param xlog If true, logarithmic axis is used.
       */
      public synchronized void setXLog(boolean xlog) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xlog = xlog;
      }
  
      /** Set the X (horizontal) range of the plot.  If this is not done
       *  explicitly, then the range is computed automatically from data
       *  available when the plot is drawn.  If min and max
       *  are identical, then the range is arbitrarily spread by 1.
       *  @param min The left extent of the range.
       *  @param max The right extent of the range.
       */
      public synchronized void setXRange(double min, double max) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _xRangeGiven = true;
          _xlowgiven = min;
          _xhighgiven = max;
          _setXRange(min, max);
      }
  
      /** Set the label for the Y (vertical) axis.
       *  @param label The label.
       */
      public synchronized void setYLabel(String label) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _ylabel = label;
      }
  
      /** Specify whether the Y axis is drawn with a logarithmic scale.
       *  If you would like to have the Y axis drawn with a
       *  logarithmic axis, then setYLog(true) should be called before
       *  adding any data points.
       *  @param ylog If true, logarithmic axis is used.
       */
      public synchronized void setYLog(boolean ylog) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _ylog = ylog;
      }
  
      /** Set the Y (vertical) range of the plot.  If this is not done
       *  explicitly, then the range is computed automatically from data
       *  available when the plot is drawn.  If min and max are identical,
       *  then the range is arbitrarily spread by 0.1.
       *  @param min The bottom extent of the range.
       *  @param max The top extent of the range.
       */
      public synchronized void setYRange(double min, double max) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _yRangeGiven = true;
          _ylowgiven = min;
          _yhighgiven = max;
          _setYRange(min, max);
      }
  
      /** Write the current data and plot configuration to the
       *  specified stream in PlotML syntax.  PlotML is an XML
       *  extension for plot data.  The written information is
       *  standalone, in that it includes the DTD (document type
       *  definition).  This makes is somewhat verbose.  To get
       *  smaller files, use the two argument version of write().
       *  The output is buffered, and is flushed and
       *  closed before exiting.  Derived classes should override
       *  writeFormat and writeData rather than this method.
       *  @param out An output stream.
       */
      public void write(OutputStream out) {
          write(out, null);
      }
  
      /** Write the current data and plot configuration to the
       *  specified stream in PlotML syntax.  PlotML is an XML
       *  scheme for plot data. The URL (relative or absolute) for the DTD is
       *  given as the second argument.  If that argument is null,
       *  then the PlotML PUBLIC DTD is referenced, resulting in a file
       *  that can be read by a PlotML parser without any external file
       *  references, as long as that parser has local access to the DTD.
       *  The output is buffered, and is flushed and
       *  closed before exiting.  Derived classes should override
       *  writeFormat and writeData rather than this method.
       *  @param out An output stream.
       *  @param dtd The reference (URL) for the DTD, or null to use the
       *   PUBLIC DTD.
       */
      public synchronized void write(OutputStream out, String dtd) {
          write(new OutputStreamWriter(out), dtd);
      }
  
      /** Write the current data and plot configuration to the
       *  specified stream in PlotML syntax.  PlotML is an XML
       *  scheme for plot data. The URL (relative or absolute) for the DTD is
       *  given as the second argument.  If that argument is null,
       *  then the PlotML PUBLIC DTD is referenced, resulting in a file
       *  that can be read by a PlotML parser without any external file
       *  references, as long as that parser has local access to the DTD.
       *  The output is buffered, and is flushed before exiting.
       *  @param out An output writer.
       *  @param dtd The reference (URL) for the DTD, or null to use the
       *   PUBLIC DTD.
       */
      public synchronized void write(Writer out, String dtd) {
          // Auto-flush is disabled.
          PrintWriter output = new PrintWriter(new BufferedWriter(out), false);
  
          if (dtd == null) {
              output.println("<?xml version=\"1.0\" standalone=\"yes\"?>");
              output
                      .println("<!DOCTYPE plot PUBLIC \"-//UC Berkeley//DTD PlotML 1//EN\"");
              output
                      .println("    \"http://ptolemy.eecs.berkeley.edu/xml/dtd/PlotML_1.dtd\">");
          } else {
              output.println("<?xml version=\"1.0\" standalone=\"no\"?>");
              output.println("<!DOCTYPE plot SYSTEM \"" + dtd + "\">");
          }
  
          output.println("<plot>");
          output.println("<!-- Ptolemy plot, version " + PTPLOT_RELEASE
                  + " , PlotML format. -->");
          writeFormat(output);
          writeData(output);
          output.println("</plot>");
          output.flush();
  
          // NOTE: We used to close the stream, but if this is part
          // of an exportMoML operation, that is the wrong thing to do.
          // if (out != System.out) {
          //    output.close();
          // }
      }
  
      /** Write plot data information to the specified output stream in PlotML.
       *  In this base class, there is no data to write, so this method
       *  returns without doing anything.
       *  @param output A buffered print writer.
       */
      public synchronized void writeData(PrintWriter output) {
      }
  
      /** Write plot format information to the specified output stream in PlotML.
       *  Derived classes should override this method to first call
       *  the parent class method, then add whatever additional format
       *  information they wish to add to the stream.
       *  @param output A buffered print writer.
       */
      public synchronized void writeFormat(PrintWriter output) {
          // NOTE: If you modify this, you should change the _DTD variable
          // accordingly.
          if (_title != null) {
              output.println("<title>" + _title + "</title>");
          }
  
          if (_xlabel != null) {
              output.println("<xLabel>" + _xlabel + "</xLabel>");
          }
  
          if (_ylabel != null) {
              output.println("<yLabel>" + _ylabel + "</yLabel>");
          }
  
          if (_xRangeGiven) {
              output.println("<xRange min=\"" + _xlowgiven + "\" max=\""
                      + _xhighgiven + "\"/>");
          }
  
          if (_yRangeGiven) {
              output.println("<yRange min=\"" + _ylowgiven + "\" max=\""
                      + _yhighgiven + "\"/>");
          }
  
          if ((_xticks != null) && (_xticks.size() > 0)) {
              output.println("<xTicks>");
  
              int last = _xticks.size() - 1;
  
              for (int i = 0; i <= last; i++) {
                  output.println("  <tick label=\""
                          + (String) _xticklabels.elementAt(i) + "\" position=\""
                          + _xticks.elementAt(i) + "\"/>");
              }
  
              output.println("</xTicks>");
          }
  
          if ((_yticks != null) && (_yticks.size() > 0)) {
              output.println("<yTicks>");
  
              int last = _yticks.size() - 1;
  
              for (int i = 0; i <= last; i++) {
                  output.println("  <tick label=\""
                          + (String) _yticklabels.elementAt(i) + "\" position=\""
                          + _yticks.elementAt(i) + "\"/>");
              }
  
              output.println("</yTicks>");
          }
  
          if (_xlog) {
              output.println("<xLog/>");
          }
  
          if (_ylog) {
              output.println("<yLog/>");
          }
  
          if (!_grid) {
              output.println("<noGrid/>");
          }
  
          if (_wrap) {
              output.println("<wrap/>");
          }
  
          if (!_usecolor) {
              output.println("<noColor/>");
          }
      }
  
     
      /** Zoom in or out to the specified rectangle.
       *  This method calls repaint().
       *  @param lowx The low end of the new X range.
       *  @param lowy The low end of the new Y range.
       *  @param highx The high end of the new X range.
       *  @param highy The high end of the new Y range.
       */
      public synchronized void zoom(double lowx, double lowy, double highx,
              double highy) {
          setXRange(lowx, highx);
          setYRange(lowy, highy);
          repaint();
      }
  
      ///////////////////////////////////////////////////////////////////
      ////                         public variables                  ////
      public static final String PTPLOT_RELEASE = "5.5-beta";
  
      ///////////////////////////////////////////////////////////////////
      ////                         protected methods                 ////
  
      /** Draw the axes using the current range, label, and title information.
       *  If the second argument is true, clear the display before redrawing.
       *  This method is called by paintComponent().  To cause it to be called
       *  you would normally call repaint(), which eventually causes
       *  paintComponent() to be called.
       *  <p>
       *  Note that this is synchronized so that points are not added
       *  by other threads while the drawing is occurring.  This method
       *  should be called only from the event dispatch thread, consistent
       *  with swing policy.
       *  @param graphics The graphics context.
       *  @param clearfirst If true, clear the plot before proceeding.
       */
      protected synchronized void _drawPlot(Graphics graphics, boolean clearfirst) {
          Rectangle bounds = getBounds();
          _drawPlot(graphics, clearfirst, bounds,_formats);
      }
  
      /** Draw the axes using the current range, label, and title information,
       *  at the size of the specified rectangle.
       *  If the second argument is true, clear the display before redrawing.
       *  This method is called by paintComponent().  To cause it to be called
       *  you would normally call repaint(), which eventually causes
       *  paintComponent() to be called.
       *  <p>
       *  Note that this is synchronized so that points are not added
       *  by other threads while the drawing is occurring.  This method
       *  should be called only from the event dispatch thread, consistent
       *  with swing policy.
       *  @param graphics The graphics context.
       *  @param clearfirst If true, clear the plot before proceeding.
       *  @param drawRect A specification of the size.
       */
      protected synchronized void _drawPlot(Graphics graphics,
              boolean clearfirst, Rectangle drawRect,Vector<Format> fmts) {
      		_formats=fmts;
          // Ignore if there is no graphics object to draw on.
          if (graphics == null) {
              return;
          }
          boolean b1=SwingUtilities.isEventDispatchThread();
          graphics.setPaintMode();
  
          /* NOTE: The following seems to be unnecessary with Swing...
           if (clearfirst) {
           // NOTE: calling clearRect() here permits the background
           // color to show through, but it messes up printing.
           // Printing results in black-on-black title and axis labels.
           graphics.setColor(_background);
           graphics.drawRect(0, 0, drawRect.width, drawRect.height);
           graphics.setColor(Color.black);
           }
           */
  
          // If an error message has been set, display it and return.
          if (_errorMsg != null) {
              int fheight = _labelFontMetrics.getHeight() + 2;
              int msgy = fheight;
              graphics.setColor(Color.black);
  
              for (int i = 0; i < _errorMsg.length; i++) {
                  graphics.drawString(_errorMsg[i], 10, msgy);
                  msgy += fheight;
                  System.err.println(_errorMsg[i]);
              }
  
              return;
          }
  
          // Make sure we have an x and y range
          if (!_xRangeGiven) {
              if (_xBottom > _xTop) {
                  // have nothing to go on.
                  _setXRange(0, 0);
              } else {
                  _setXRange(_xBottom, _xTop);
              }
          }
  
          if (!_yRangeGiven) {
              if (_yBottom > _yTop) {
                  // have nothing to go on.
                  _setYRange(0, 0);
              } else {
                  _setYRange(_yBottom, _yTop);
              }
          }
  
          // If user specified a plot rectangle, compute
          // a working plot rectangle which lies inside the
          // drawRect at the user specified coordinates
          Rectangle workingPlotRectangle = null;
  
          if (_specifiedPlotRectangle != null) {
              workingPlotRectangle = new Rectangle(Math.max(0,
                      _specifiedPlotRectangle.x), Math.max(0,
                      _specifiedPlotRectangle.y), Math.min(drawRect.width,
                      _specifiedPlotRectangle.width), Math.min(drawRect.height,
                      _specifiedPlotRectangle.height));
          }
  
          // Vertical space for title, if appropriate.
          // NOTE: We assume a one-line title.
          int titley = 0;
          int titlefontheight = _titleFontMetrics.getHeight();
  
          if (_title == null) {
              // NOTE: If the _title is null, then set it to the empty
              // string to solve the problem where the fill button overlaps
              // the legend if there is no title.  The fix here would
              // be to modify the legend printing text so that it takes
              // into account the case where there is no title by offsetting
              // just enough for the button.
              _title = "";
          }
  
          if ((_title != null) || (_yExp != 0)) {
              titley = titlefontheight + _topPadding;
          }
  
          // Number of vertical tick marks depends on the height of the font
          // for labeling ticks and the height of the window.
          Font previousFont = graphics.getFont();
          graphics.setFont(_labelFont);
          graphics.setColor(_foreground); // foreground color not set here  --Rob.
  
          int labelheight = _labelFontMetrics.getHeight();
          int halflabelheight = labelheight / 2;
  
          // Draw scaling annotation for x axis.
          // NOTE: 5 pixel padding on bottom.
          int ySPos = drawRect.height - 5;
          int xSPos = drawRect.width - _rightPadding;
  
          if (_xlog) {
              _xExp = (int) Math.floor(_xtickMin);
          }
  
          if ((_xExp != 0) && (_xticks == null)) {
              String superscript = Integer.toString(_xExp);
              xSPos -= _superscriptFontMetrics.stringWidth(superscript);
              graphics.setFont(_superscriptFont);
  
              if (!_xlog) {
                  graphics
                          .drawString(superscript, xSPos, ySPos - halflabelheight);
                  xSPos -= _labelFontMetrics.stringWidth("x10");
                  graphics.setFont(_labelFont);
                  graphics.drawString("x10", xSPos, ySPos);
              }
  
              // NOTE: 5 pixel padding on bottom
              _bottomPadding = ((3 * labelheight) / 2) + 5;
          }
  
          // NOTE: 5 pixel padding on the bottom.
          if ((_xlabel != null) && (_bottomPadding < (labelheight + 5))) {
              _bottomPadding = labelheight + 5;
          }
  
          // Compute the space needed around the plot, starting with vertical.
          // NOTE: padding of 5 pixels below title.
          if (workingPlotRectangle != null) {
              _uly = workingPlotRectangle.y;
          } else {
              _uly = titley + 5;
          }
  
          // NOTE: 3 pixels above bottom labels.
          if (workingPlotRectangle != null) {
              _lry = workingPlotRectangle.y + workingPlotRectangle.height;
          } else {
              _lry = drawRect.height - labelheight - _bottomPadding - 3;
          }
  
          int height = _lry - _uly;
          _yscale = height / (_yMax - _yMin);
          _ytickscale = height / (_ytickMax - _ytickMin);
  
          ////////////////// vertical axis
          // Number of y tick marks.
          // NOTE: subjective spacing factor.
          int ny = 2 + (height / (labelheight + 10));
  
          // Compute y increment.
          double yStep = _roundUp((_ytickMax - _ytickMin) / ny);
  
          // Compute y starting point so it is a multiple of yStep.
          double yStart = yStep * Math.ceil(_ytickMin / yStep);
  
          // NOTE: Following disables first tick.  Not a good idea?
          // if (yStart == _ytickMin) yStart += yStep;
          // Define the strings that will label the y axis.
          // Meanwhile, find the width of the widest label.
          // The labels are quantized so that they don't have excess resolution.
          int widesty = 0;
  
          // These do not get used unless ticks are automatic, but the
          // compiler is not smart enough to allow us to reference them
          // in two distinct conditional clauses unless they are
          // allocated outside the clauses.
          String[] ylabels = new String[ny];
          int[] ylabwidth = new int[ny];
  
          int ind = 0;
  
          if (_yticks == null) {
              Vector ygrid = null;
  
              if (_ylog) {
                  ygrid = _gridInit(yStart, yStep, true, null);
              }
  
              // automatic ticks
              // First, figure out how many digits after the decimal point
              // will be used.
              int numfracdigits = _numFracDigits(yStep);
  
              // NOTE: Test cases kept in case they are needed again.
              // System.out.println("0.1 with 3 digits: " + _formatNum(0.1, 3));
              // System.out.println("0.0995 with 3 digits: " +
              //                    _formatNum(0.0995, 3));
              // System.out.println("0.9995 with 3 digits: " +
              //                    _formatNum(0.9995, 3));
              // System.out.println("1.9995 with 0 digits: " +
              //                    _formatNum(1.9995, 0));
              // System.out.println("1 with 3 digits: " + _formatNum(1, 3));
              // System.out.println("10 with 0 digits: " + _formatNum(10, 0));
              // System.out.println("997 with 3 digits: " + _formatNum(997, 3));
              // System.out.println("0.005 needs: " + _numFracDigits(0.005));
              // System.out.println("1 needs: " + _numFracDigits(1));
              // System.out.println("999 needs: " + _numFracDigits(999));
              // System.out.println("999.0001 needs: "+_numFracDigits(999.0001));
              // System.out.println("0.005 integer digits: " +
              //                    _numIntDigits(0.005));
              // System.out.println("1 integer digits: " + _numIntDigits(1));
              // System.out.println("999 integer digits: " + _numIntDigits(999));
              // System.out.println("-999.0001 integer digits: " +
              //                    _numIntDigits(999.0001));
              double yTmpStart = yStart;
  
              if (_ylog) {
                  yTmpStart = _gridStep(ygrid, yStart, yStep, _ylog);
              }
  
              for (double ypos = yTmpStart; ypos <= _ytickMax; ypos = _gridStep(
                      ygrid, ypos, yStep, _ylog)) {
                  // Prevent out of bounds exceptions
                  if (ind >= ny) {
                      break;
                  }
  
                  String yticklabel;
  
                  if (_ylog) {
                      yticklabel = _formatLogNum(ypos, numfracdigits);
                  } else {
                      yticklabel = _formatNum(ypos, numfracdigits);
                  }
  
                  ylabels[ind] = yticklabel;
  
                  int lw = _labelFontMetrics.stringWidth(yticklabel);
                  ylabwidth[ind++] = lw;
  
                  if (lw > widesty) {
                      widesty = lw;
                  }
              }
          } else {
              // explicitly specified ticks
              Enumeration nl = _yticklabels.elements();
  
              while (nl.hasMoreElements()) {
                  String label = (String) nl.nextElement();
                  int lw = _labelFontMetrics.stringWidth(label);
  
                  if (lw > widesty) {
                      widesty = lw;
                  }
              }
          }
  
          // Next we do the horizontal spacing.
          if (workingPlotRectangle != null) {
              _ulx = workingPlotRectangle.x;
          } else {
              if (_ylabel != null) {
                  _ulx = widesty + _labelFontMetrics.stringWidth("W")
                          + _leftPadding;
              } else {
                  _ulx = widesty + _leftPadding;
              }
          }
  
          int legendwidth = _drawLegend(graphics, drawRect.width - _rightPadding,
                  _uly);
  
          if (workingPlotRectangle != null) {
              _lrx = workingPlotRectangle.x + workingPlotRectangle.width;
          } else {
              _lrx = drawRect.width - legendwidth - _rightPadding;
          }
  
          int width = _lrx - _ulx;
          _xscale = width / (_xMax - _xMin);
  
          _xtickscale = width / (_xtickMax - _xtickMin);
  
          // Background for the plotting rectangle.
          // Always use a white background because the dataset colors
          // were designed for a white background.
          graphics.setColor(Color.white);
          graphics.fillRect(_ulx, _uly, width, height);
  
          graphics.setColor(_foreground);
          graphics.drawRect(_ulx, _uly, width, height);
  
          // NOTE: subjective tick length.
          int tickLength = 5;
          int xCoord1 = _ulx + tickLength;
          int xCoord2 = _lrx - tickLength;
  
          if (_yticks == null) {
              // auto-ticks
              Vector ygrid = null;
              double yTmpStart = yStart;
  
              if (_ylog) {
                  ygrid = _gridInit(yStart, yStep, true, null);
                  yTmpStart = _gridStep(ygrid, yStart, yStep, _ylog);
                  ny = ind;
              }
  
              ind = 0;
  
              // Set to false if we don't need the exponent
              boolean needExponent = _ylog;
  
              for (double ypos = yTmpStart; ypos <= _ytickMax; ypos = _gridStep(
                      ygrid, ypos, yStep, _ylog)) {
                  // Prevent out of bounds exceptions
                  if (ind >= ny) {
                      break;
                  }
  
                  int yCoord1 = _lry - (int) ((ypos - _ytickMin) * _ytickscale);
  
                  // The lowest label is shifted up slightly to avoid
                  // colliding with x labels.
                  int offset = 0;
  
                  if ((ind > 0) && !_ylog) {
                      offset = halflabelheight;
                  }
  
                  graphics.drawLine(_ulx, yCoord1, xCoord1, yCoord1);
                  graphics.drawLine(_lrx, yCoord1, xCoord2, yCoord1);
  
                  if (_grid && (yCoord1 != _uly) && (yCoord1 != _lry)) {
                      graphics.setColor(Color.lightGray);
                      graphics.drawLine(xCoord1, yCoord1, xCoord2, yCoord1);
                      graphics.setColor(_foreground);
                  }
  
                  // Check to see if any of the labels printed contain
                  // the exponent.  If we don't see an exponent, then print it.
                  if (_ylog && (ylabels[ind].indexOf('e') != -1)) {
                      needExponent = false;
                  }
  
                  // NOTE: 4 pixel spacing between axis and labels.
                  graphics.drawString(ylabels[ind], _ulx - ylabwidth[ind++] - 4,
                          yCoord1 + offset);
              }
  
              if (_ylog) {
                  // Draw in grid lines that don't have labels.
                  Vector unlabeledgrid = _gridInit(yStart, yStep, false, ygrid);
  
                  if (unlabeledgrid.size() > 0) {
                      // If the step is greater than 1, clamp it to 1 so that
                      // we draw the unlabeled grid lines for each
                      //integer interval.
                      double tmpStep = (yStep > 1.0) ? 1.0 : yStep;
  
                      for (double ypos = _gridStep(unlabeledgrid, yStart,
                              tmpStep, _ylog); ypos <= _ytickMax; ypos = _gridStep(
                              unlabeledgrid, ypos, tmpStep, _ylog)) {
                          int yCoord1 = _lry
                                  - (int) ((ypos - _ytickMin) * _ytickscale);
  
                          if (_grid && (yCoord1 != _uly) && (yCoord1 != _lry)) {
                              graphics.setColor(Color.lightGray);
                              graphics.drawLine(_ulx + 1, yCoord1, _lrx - 1,
                                      yCoord1);
                              graphics.setColor(_foreground);
                          }
                      }
                  }
  
                  if (needExponent) {
                      // We zoomed in, so we need the exponent
                      _yExp = (int) Math.floor(yTmpStart);
                  } else {
                      _yExp = 0;
                  }
              }
  
              // Draw scaling annotation for y axis.
              if (_yExp != 0) {
                  graphics.drawString("x10", 2, titley);
                  graphics.setFont(_superscriptFont);
                  graphics.drawString(Integer.toString(_yExp), _labelFontMetrics
                          .stringWidth("x10") + 2, titley - halflabelheight);
                  graphics.setFont(_labelFont);
              }
          } else {
              // ticks have been explicitly specified
              Enumeration nt = _yticks.elements();
              Enumeration nl = _yticklabels.elements();
  
              while (nl.hasMoreElements()) {
                  String label = (String) nl.nextElement();
                  double ypos = ((Double) (nt.nextElement())).doubleValue();
  
                  if ((ypos > _yMax) || (ypos < _yMin)) {
                      continue;
                  }
  
                  int yCoord1 = _lry - (int) ((ypos - _yMin) * _yscale);
                  int offset = 0;
  
                  if (ypos < (_lry - labelheight)) {
                      offset = halflabelheight;
                  }
  
                  graphics.drawLine(_ulx, yCoord1, xCoord1, yCoord1);
                  graphics.drawLine(_lrx, yCoord1, xCoord2, yCoord1);
  
                  if (_grid && (yCoord1 != _uly) && (yCoord1 != _lry)) {
                      graphics.setColor(Color.lightGray);
                      graphics.drawLine(xCoord1, yCoord1, xCoord2, yCoord1);
                      graphics.setColor(_foreground);
                  }
  
                  // NOTE: 3 pixel spacing between axis and labels.
                  graphics.drawString(label, _ulx
                          - _labelFontMetrics.stringWidth(label) - 3, yCoord1
                          + offset);
              }
          }
  
          //////////////////// horizontal axis
          int yCoord1 = _uly + tickLength;
          int yCoord2 = _lry - tickLength;
          int charwidth = _labelFontMetrics.stringWidth("8");
  
          if (_xticks == null) {
              // auto-ticks
              // Number of x tick marks.
              // Need to start with a guess and converge on a solution here.
              int nx = 10;
              double xStep = 0.0;
              int numfracdigits = 0;
  
              if (_xlog) {
                  // X axes log labels will be at most 6 chars: -1E-02
                  nx = 2 + (width / ((charwidth * 6) + 10));
              } else {
                  // Limit to 10 iterations
                  int count = 0;
  
                  while (count++ <= 10) {
                      xStep = _roundUp((_xtickMax - _xtickMin) / nx);
  
                      // Compute the width of a label for this xStep
                      numfracdigits = _numFracDigits(xStep);
  
                      // Number of integer digits is the maximum of two endpoints
                      int intdigits = _numIntDigits(_xtickMax);
                      int inttemp = _numIntDigits(_xtickMin);
  
                      if (intdigits < inttemp) {
                          intdigits = inttemp;
                      }
  
                      // Allow two extra digits (decimal point and sign).
                      int maxlabelwidth = charwidth
                              * (numfracdigits + 2 + intdigits);
  
                      // Compute new estimate of number of ticks.
                      int savenx = nx;
  
                      // NOTE: 10 additional pixels between labels.
                      // NOTE: Try to ensure at least two tick marks.
                      nx = 2 + (width / (maxlabelwidth + 10));
  
                      if (((nx - savenx) <= 1) || ((savenx - nx) <= 1)) {
                          break;
                      }
                  }
              }
  
              xStep = _roundUp((_xtickMax - _xtickMin) / nx);
              numfracdigits = _numFracDigits(xStep);
  
              // Compute x starting point so it is a multiple of xStep.
              double xStart = xStep * Math.ceil(_xtickMin / xStep);
  
              // NOTE: Following disables first tick.  Not a good idea?
              // if (xStart == _xMin) xStart += xStep;
              Vector xgrid = null;
              double xTmpStart = xStart;
  
              if (_xlog) {
                  xgrid = _gridInit(xStart, xStep, true, null);
  
                  //xgrid = _gridInit(xStart, xStep);
                  xTmpStart = _gridRoundUp(xgrid, xStart);
              }
  
              // Set to false if we don't need the exponent
              boolean needExponent = _xlog;
  
              // Label the x axis.  The labels are quantized so that
              // they don't have excess resolution.
              for (double xpos = xTmpStart; xpos <= _xtickMax; xpos = _gridStep(
                      xgrid, xpos, xStep, _xlog)) {
                  String xticklabel;
  
                  if (_xlog) {
                      xticklabel = _formatLogNum(xpos, numfracdigits);
  
                      if (xticklabel.indexOf('e') != -1) {
                          needExponent = false;
                      }
                  } else {
                      xticklabel = _formatNum(xpos, numfracdigits);
                  }
  
                  xCoord1 = _ulx + (int) ((xpos - _xtickMin) * _xtickscale);
                  graphics.drawLine(xCoord1, _uly, xCoord1, yCoord1);
                  graphics.drawLine(xCoord1, _lry, xCoord1, yCoord2);
  
                  if (_grid && (xCoord1 != _ulx) && (xCoord1 != _lrx)) {
                      graphics.setColor(Color.lightGray);
                      graphics.drawLine(xCoord1, yCoord1, xCoord1, yCoord2);
                      graphics.setColor(_foreground);
                  }
  
                  int labxpos = xCoord1
                          - (_labelFontMetrics.stringWidth(xticklabel) / 2);
  
                  // NOTE: 3 pixel spacing between axis and labels.
                  graphics
                          .drawString(xticklabel, labxpos, _lry + 3 + labelheight);
              }
  
              if (_xlog) {
                  // Draw in grid lines that don't have labels.
                  // If the step is greater than 1, clamp it to 1 so that
                  // we draw the unlabeled grid lines for each
                  // integer interval.
                  double tmpStep = (xStep > 1.0) ? 1.0 : xStep;
  
                  // Recalculate the start using the new step.
                  xTmpStart = tmpStep * Math.ceil(_xtickMin / tmpStep);
  
                  Vector unlabeledgrid = _gridInit(xTmpStart, tmpStep, false,
                          xgrid);
  
                  if (unlabeledgrid.size() > 0) {
                      for (double xpos = _gridStep(unlabeledgrid, xTmpStart,
                              tmpStep, _xlog); xpos <= _xtickMax; xpos = _gridStep(
                              unlabeledgrid, xpos, tmpStep, _xlog)) {
                          xCoord1 = _ulx
                                  + (int) ((xpos - _xtickMin) * _xtickscale);
  
                          if (_grid && (xCoord1 != _ulx) && (xCoord1 != _lrx)) {
                              graphics.setColor(Color.lightGray);
                              graphics.drawLine(xCoord1, _uly + 1, xCoord1,
                                      _lry - 1);
                              graphics.setColor(_foreground);
                          }
                      }
                  }
  
                  if (needExponent) {
                      _xExp = (int) Math.floor(xTmpStart);
                      graphics.setFont(_superscriptFont);
                      graphics.drawString(Integer.toString(_xExp), xSPos, ySPos
                              - halflabelheight);
                      xSPos -= _labelFontMetrics.stringWidth("x10");
                      graphics.setFont(_labelFont);
                      graphics.drawString("x10", xSPos, ySPos);
                  } else {
                      _xExp = 0;
                  }
              }
          } else {
              // ticks have been explicitly specified
              Enumeration nt = _xticks.elements();
              Enumeration nl = _xticklabels.elements();
  
              // Code contributed by Jun Wu (jwu@inin.com.au)
              double preLength = 0.0;
  
              while (nl.hasMoreElements()) {
                  String label = (String) nl.nextElement();
                  double xpos = ((Double) (nt.nextElement())).doubleValue();
  
                  // If xpos is out of range, ignore.
                  if ((xpos > _xMax) || (xpos < _xMin)) {
                      continue;
                  }
  
                  // Find the center position of the label.
                  xCoord1 = _ulx + (int) ((xpos - _xMin) * _xscale);
  
                  // Find  the start position of x label.
                  int labxpos = xCoord1
                          - (_labelFontMetrics.stringWidth(label) / 2);
  
                  // If the labels are not overlapped, proceed.
                  if (labxpos > preLength) {
                      // calculate the length of the label
                      preLength = xCoord1
                              + (_labelFontMetrics.stringWidth(label) / 2) + 10;
  
                      // Draw the label.
                      // NOTE: 3 pixel spacing between axis and labels.
                      graphics.drawString(label, labxpos, _lry + 3 + labelheight);
  
                      // Draw the label mark on the axis
                      graphics.drawLine(xCoord1, _uly, xCoord1, yCoord1);
                      graphics.drawLine(xCoord1, _lry, xCoord1, yCoord2);
  
                      // Draw the grid line
                      if (_grid && (xCoord1 != _ulx) && (xCoord1 != _lrx)) {
                          graphics.setColor(Color.lightGray);
                          graphics.drawLine(xCoord1, yCoord1, xCoord1, yCoord2);
                          graphics.setColor(_foreground);
                      }
                  }
              }
          }
  
          //////////////////// Draw title and axis labels now.
          // Center the title and X label over the plotting region, not
          // the window.
          graphics.setColor(_foreground);
  
          if (_title != null) {
              graphics.setFont(_titleFont);
  
              int titlex = _ulx
                      + ((width - _titleFontMetrics.stringWidth(_title)) / 2);
              graphics.drawString(_title, titlex, titley);
          }
  
          graphics.setFont(_labelFont);
  
          if (_xlabel != null) {
              int labelx = _ulx
                      + ((width - _labelFontMetrics.stringWidth(_xlabel)) / 2);
              graphics.drawString(_xlabel, labelx, ySPos);
          }
  
          int charcenter = 2 + (_labelFontMetrics.stringWidth("W") / 2);
  
          if (_ylabel != null) {
              int yl = _ylabel.length();
  
              if (graphics instanceof Graphics2D) {
                  int starty = (_uly + ((_lry - _uly) / 2) + (_labelFontMetrics
                          .stringWidth(_ylabel) / 2))
                          - charwidth;
                  Graphics2D g2d = (Graphics2D) graphics;
  
                  // NOTE: Fudge factor so label doesn't touch axis labels.
                  int startx = (charcenter + halflabelheight) - 2;
                  g2d.rotate(Math.toRadians(-90), startx, starty);
                  g2d.drawString(_ylabel, startx, starty);
                  g2d.rotate(Math.toRadians(90), startx, starty);
              } else {
                  // Not graphics 2D, no support for rotation.
                  // Vertical label is fairly complex to draw.
                  int starty = (_uly + ((_lry - _uly) / 2))
                          - (yl * halflabelheight) + labelheight;
  
                  for (int i = 0; i < yl; i++) {
                      String nchar = _ylabel.substring(i, i + 1);
                      int cwidth = _labelFontMetrics.stringWidth(nchar);
                      graphics.drawString(nchar, charcenter - (cwidth / 2),
                              starty);
                      starty += labelheight;
                  }
              }
          }
  
          graphics.setFont(previousFont);
      }
  
      /** Put a mark corresponding to the specified dataset at the
       *  specified x and y position.   The mark is drawn in the
       *  current color.  In this base class, a point is a
       *  filled rectangle 6 pixels across.  Note that marks greater than
       *  about 6 pixels in size will not look very good since they will
       *  overlap axis labels and may not fit well in the legend.   The
       *  <i>clip</i> argument, if <code>true</code>, states
       *  that the point should not be drawn if
       *  it is out of range.
       *
       *  Note that this method is not synchronized, so the caller should be.
       *  Moreover this method should always be called from the event thread
       *  when being used to write to the screen.
       *
       *  @param graphics The graphics context.
       *  @param dataset The index of the data set.
       *  @param xpos The X position.
       *  @param ypos The Y position.
       *  @param clip If true, do not draw if out of range.
       */
      protected void _drawPoint(Graphics graphics, int dataset, long xpos,
              long ypos, boolean clip) {
          // Ignore if there is no graphics object to draw on.
          if (graphics == null) {
              return;
          }
  
          boolean pointinside = (ypos <= _lry) && (ypos >= _uly)
                  && (xpos <= _lrx) && (xpos >= _ulx);
  
          if (!pointinside && clip) {
              return;
          }
  
          graphics.fillRect((int) xpos - 6, (int) ypos - 6, 6, 6);
      }
  
      /** Display basic information in its own window.
       */
      protected void _help() {
          String message = "Ptolemy plot package\n"
                  + "By: Edward A. Lee\n"
                  + "and Christopher Brooks\n"
                  + "Version "
                  + PTPLOT_RELEASE
                  + ", Build: $Id: PlotBox.java,v 1.273 2006/03/29 00:01:46 cxh Exp $\n\n"
                  + "Key bindings:\n"
                  + "   Cntrl-c:  copy plot to clipboard (EPS format), if permitted\n"
                  + "   D: dump plot data to standard out\n"
                  + "   E: export plot to standard out (EPS format)\n"
                  + "   F: fill plot\n"
                  + "   H or ?: print help message (this message)\n"
                  + "   Cntrl-D or Q: quit\n" + "For more information, see\n"
                  + "http://ptolemy.eecs.berkeley.edu/java/ptplot\n";
          JOptionPane.showMessageDialog(this, message,
                  "Ptolemy Plot Help Window", JOptionPane.INFORMATION_MESSAGE);
      }
  
      /** Parse a line that gives plotting information.  In this base
       *  class, only lines pertaining to the title and labels are processed.
       *  Everything else is ignored. Return true if the line is recognized.
       *  It is not synchronized, so its caller should be.
       *  @param line A line of text.
       */
      protected boolean _parseLine(String line) {
          // If you modify this method, you should also modify write()
          // We convert the line to lower case so that the command
          // names are case insensitive.
          String lcLine = new String(line.toLowerCase());
  
          if (lcLine.startsWith("#")) {
              // comment character
              return true;
          } else if (lcLine.startsWith("titletext:")) {
              setTitle((line.substring(10)).trim());
              return true;
          } else if (lcLine.startsWith("title:")) {
              // Tolerate alternative tag.
              setTitle((line.substring(6)).trim());
              return true;
          } else if (lcLine.startsWith("xlabel:")) {
              setXLabel((line.substring(7)).trim());
              return true;
          } else if (lcLine.startsWith("ylabel:")) {
              setYLabel((line.substring(7)).trim());
              return true;
          } else if (lcLine.startsWith("xrange:")) {
              int comma = line.indexOf(",", 7);
  
              if (comma > 0) {
                  String min = (line.substring(7, comma)).trim();
                  String max = (line.substring(comma + 1)).trim();
  
                  try {
                      Double dmin = new Double(min);
                      Double dmax = new Double(max);
                      setXRange(dmin.doubleValue(), dmax.doubleValue());
                  } catch (NumberFormatException e) {
                      // ignore if format is bogus.
                  }
              }
  
              return true;
          } else if (lcLine.startsWith("yrange:")) {
              int comma = line.indexOf(",", 7);
  
              if (comma > 0) {
                  String min = (line.substring(7, comma)).trim();
                  String max = (line.substring(comma + 1)).trim();
  
                  try {
                      Double dmin = new Double(min);
                      Double dmax = new Double(max);
                      setYRange(dmin.doubleValue(), dmax.doubleValue());
                  } catch (NumberFormatException e) {
                      // ignore if format is bogus.
                  }
              }
  
              return true;
          } else if (lcLine.startsWith("xticks:")) {
              // example:
              // XTicks "label" 0, "label" 1, "label" 3
              _parsePairs(line.substring(7), true);
              return true;
          } else if (lcLine.startsWith("yticks:")) {
              // example:
              // YTicks "label" 0, "label" 1, "label" 3
              _parsePairs(line.substring(7), false);
              return true;
          } else if (lcLine.startsWith("xlog:")) {
              if (lcLine.indexOf("off", 5) >= 0) {
                  _xlog = false;
              } else {
                  _xlog = true;
              }
  
              return true;
          } else if (lcLine.startsWith("ylog:")) {
              if (lcLine.indexOf("off", 5) >= 0) {
                  _ylog = false;
              } else {
                  _ylog = true;
              }
  
              return true;
          } else if (lcLine.startsWith("grid:")) {
              if (lcLine.indexOf("off", 5) >= 0) {
                  _grid = false;
              } else {
                  _grid = true;
              }
  
              return true;
          } else if (lcLine.startsWith("wrap:")) {
              if (lcLine.indexOf("off", 5) >= 0) {
                  _wrap = false;
              } else {
                  _wrap = true;
              }
  
              return true;
          } else if (lcLine.startsWith("color:")) {
              if (lcLine.indexOf("off", 6) >= 0) {
                  _usecolor = false;
              } else {
                  _usecolor = true;
              }
  
              return true;
          }
  
          return false;
      }
  
      
  
      /** Set the padding multiple.
       *  The plot rectangle can be "padded" in each direction -x, +x, -y, and
       *  +y.  If the padding is set to 0.05 (and the padding is used), then
       *  there is 10% more length on each axis than set by the setXRange() and
       *  setYRange() methods, 5% in each direction.
       *  @param padding The padding multiple.
       */
      protected void _setPadding(double padding) {
          // Changing legend means we need to repaint the offscreen buffer.
          _plotImage = null;
  
          _padding = padding;
      }
  
    
  
      ///////////////////////////////////////////////////////////////////
      ////                         protected variables               ////
      // The range of the data to be plotted.
      protected transient double _yMax = 0;
  
      ///////////////////////////////////////////////////////////////////
      ////                         protected variables               ////
      // The range of the data to be plotted.
      protected transient double _yMin = 0;
  
      ///////////////////////////////////////////////////////////////////
      ////                         protected variables               ////
      // The range of the data to be plotted.
      protected transient double _xMax = 0;
  
      ///////////////////////////////////////////////////////////////////
      ////                         protected variables               ////
      // The range of the data to be plotted.
      protected transient double _xMin = 0;
  
      /** The factor we pad by so that we don't plot points on the axes.
       */
      protected double _padding = 0.05;
  
      // Whether the ranges have been given.
      protected transient boolean _xRangeGiven = false;
  
      protected transient boolean _yRangeGiven = false;
  
      protected transient boolean _rangesGivenByZooming = false;
  
      /** @serial The given X and Y ranges.
       * If they have been given the top and bottom of the x and y ranges.
       * This is different from _xMin and _xMax, which actually represent
       * the range of data that is plotted.  This represents the range
       * specified (which may be different due to zooming).
       */
      protected double _xlowgiven;
  
      /** @serial The given X and Y ranges.
       * If they have been given the top and bottom of the x and y ranges.
       * This is different from _xMin and _xMax, which actually represent
       * the range of data that is plotted.  This represents the range
       * specified (which may be different due to zooming).
       */
      protected double _xhighgiven;
  
      /** @serial The given X and Y ranges.
       * If they have been given the top and bottom of the x and y ranges.
       * This is different from _xMin and _xMax, which actually represent
       * the range of data that is plotted.  This represents the range
       * specified (which may be different due to zooming).
       */
      protected double _ylowgiven;
  
      /** @serial The given X and Y ranges.
       * If they have been given the top and bottom of the x and y ranges.
       * This is different from _xMin and _xMax, which actually represent
       * the range of data that is plotted.  This represents the range
       * specified (which may be different due to zooming).
       */
      protected double _yhighgiven;
  
      /** @serial The minimum X value registered so for, for auto ranging. */
      protected double _xBottom = Double.MAX_VALUE;
  
      /** @serial The maximum X value registered so for, for auto ranging. */
      protected double _xTop = -Double.MAX_VALUE;
  
      /** @serial The minimum Y value registered so for, for auto ranging. */
      protected double _yBottom = Double.MAX_VALUE;
  
      /** @serial The maximum Y value registered so for, for auto ranging. */
      protected double _yTop = -Double.MAX_VALUE;
  
      /** @serial Whether to draw the axes using a logarithmic scale. */
      protected boolean _xlog = false;
  
      /** @serial Whether to draw the axes using a logarithmic scale. */
      protected boolean _ylog = false;
  
      // For use in calculating log base 10. A log times this is a log base 10.
      protected static final double _LOG10SCALE = 1 / Math.log(10);
  
      /** @serial Whether to draw a background grid. */
      protected boolean _grid = true;
  
      /** @serial Whether to wrap the X axis. */
      protected boolean _wrap = false;
  
      /** @serial The high range of the X axis for wrapping. */
      protected double _wrapHigh;
  
      /** @serial The low range of the X axis for wrapping. */
      protected double _wrapLow;
  
      /** @serial Color of the background, settable from HTML. */
      protected Color _background = Color.white;
  
      /** @serial Color of the foreground, settable from HTML. */
      protected Color _foreground = Color.black;
  
      /** @serial Top padding.
       *  Derived classes can increment these to make space around the plot.
       */
      protected int _topPadding = 10;
  
      /** @serial Bottom padding.
       *  Derived classes can increment these to make space around the plot.
       */
      protected int _bottomPadding = 5;
  
      /** @serial Right padding.
       *  Derived classes can increment these to make space around the plot.
       */
      protected int _rightPadding = 10;
  
      /** @serial Left padding.
       *  Derived classes can increment these to make space around the plot.
       */
      protected int _leftPadding = 10;
  
      // The naming convention is: "_ulx" = "upper left x", where "x" is
      // the horizontal dimension.
  
      /** The x value of the upper left corner of the plot rectangle in pixels. */
      protected int _ulx = 1;
  
      /** The y value of the upper left corner of the plot rectangle in pixels. */
      protected int _uly = 1;
  
      /** The x value of the lower right corner of
       * the plot rectangle in pixels. */
      protected int _lrx = 100;
  
      /** The y value of the lower right corner of
       * the plot rectangle in pixels. */
      protected int _lry = 100;
  
      /** User specified plot rectangle, null if none specified.
       *  @see #setPlotRectangle(Rectangle)
       */
      protected Rectangle _specifiedPlotRectangle = null;
  
      /** Scaling used for the vertical axis in plotting points.
       *  The units are pixels/unit, where unit is the units of the Y axis.
       */
      protected double _yscale = 1.0;
  
      /** Scaling used for the horizontal axis in plotting points.
       *  The units are pixels/unit, where unit is the units of the X axis.
       */
      protected double _xscale = 1.0;
  
      /** @serial Indicator whether to use _colors. */
      protected boolean _usecolor = true;
  
      // Default _colors, by data set.
      // There are 11 colors so that combined with the
      // 10 marks of the Plot class, we can distinguish 110
      // distinct data sets.
      static protected ArrayList<Color> _colors = new ArrayList<Color>(Arrays.asList(
      		new Color(0xff0000), // red
  