Package com.googlecode.gchart.client

Source Code of com.googlecode.gchart.client.GChart$Curve$Point

/*
* Copyright 2007,2008 John C. Gunther
*
* Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*
*/
package com.googlecode.gchart.client;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.Date;

/**
* A GChart can represent and display a line chart, a bar chart,
* a pie chart, an area chart, or a chart that contains arbitrary
* combinations of line, bar, pie, and/or area based curves.
*
* <p>
* For detailed examples, with screen shots, visit the
* <a href="package-summary.html#ChartGallery">
* Chart Gallery</a>.
*
* <p>
* For detailed instructions on how to integrate Client-side GChart
* into your GWT application, see
* <a href="package-summary.html#InstallingGChart">
* Installing Client-side GChart</a>.
*
* <p>
* <b>CSS Style Rule</b>
* <ul>
* .gchart-GChart { the GChart's primary top-level styles }
* </ul>
*
*
* It is sometimes more natural to consider certain CSS
* attributes as properties of a GChart Java object. So, GChart
* supports "CSS convenience methods" that let you (optionally) use
* Java to specify GChart CSS attributes such as
* <tt>border-color</tt> and <tt>background-color</tt>. See
* {@link #USE_CSS USE_CSS} for a detailed description of these
* CSS convenience methods--which won't interfere with standard
* CSS-based specifications if you never invoke them.
*
*
**/

public class GChart extends Composite {

  
   /**
    ** Defines the location of a data point's annotation (text
    ** label) relative to the location of that point's symbol.
    ** The default annotation location is {@link
    ** AnnotationLocation#SOUTH SOUTH}.  The "Field Summary"
    ** section below lists all available annotation locations.
    **
    ** <p>
    **
    ** You can further adjust the position of a point's
    ** annotation by specifying non-zero positional shifts via
    ** the <tt>setAnnotationXShift</tt> and
    ** <tt>setAnnotationYShift</tt> methods.
    **
    ** @see Curve.Point#setAnnotationLocation Point.setAnnotationLocation
    ** @see Curve.Point#setAnnotationXShift Point.setAnnotationXShift
    ** @see Curve.Point#setAnnotationYShift Point.setAnnotationYShift
    **
    **/
   public static final class AnnotationLocation {
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned so as to be centered on the symbol
       ** used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation CENTER =
        new AnnotationLocation(0,0);

      private static final AnnotationLocation north =
         new AnnotationLocation(0,-1);
      private static final AnnotationLocation west =
        new AnnotationLocation(-1, 0);
      private static final AnnotationLocation south =
          new AnnotationLocation(0, 1);
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just above, and centered horizontally on,
       ** vertical bars that grow down from a horizontal
       ** baseline, and just below, and centered horizontally on,
       ** vertical bars that grow up from a horizontal baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.NORTH</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ
       ** <tt>VBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          CLOSEST_TO_HORIZONTAL_BASELINE = north;
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just to the right of, and centered vertically
       ** on, horizontal bars that grow left from a vertical
       ** baseline, and just to the left of, and centered
       ** vertically on, horizontal bars that grow right from a
       ** vertical baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.WEST</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ the
       ** <tt>HBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
       **
       **/
      
      public static final AnnotationLocation
         CLOSEST_TO_VERTICAL_BASELINE = west;
     
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of, and vertically
       ** centered on, the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation EAST =
         new AnnotationLocation(1, 0);

      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just below, and centered horizontally on,
       ** vertical bars that grow down from a horizontal
       ** baseline, and just above, and centered horizontally on,
       ** vertical bars that grow up from a horizontal baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.SOUTH</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ
       ** <tt>VBAR_BASELINE_*</tt> symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#VBAR_BASELINE_CENTER SymbolType.VBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          FARTHEST_FROM_HORIZONTAL_BASELINE = south;
     
      /**
       ** Specifies that a point's annotation (label) should be
       ** placed just to the left of, and centered vertically on,
       ** horizontal bars that grow left from a vertical
       ** baseline, and just to the right of, and centered
       ** vertically on, horizontal bars that grow right from a
       ** vertical baseline.
       **
       ** <p>
       **
       ** This another name for
       ** <tt>AnnotationLocation.EAST</tt>. Its sole purpose is
       ** to clarify/document the behavior of this location type
       ** when used in conjunction with curves that employ the
       ** <tt>HBAR_BASELINE_*</tt> family of symbol types.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see SymbolType#HBAR_BASELINE_CENTER SymbolType.HBAR_BASELINE_CENTER
       **
       **/
      public static final AnnotationLocation
          FARTHEST_FROM_VERTICAL_BASELINE = EAST;
     

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just inside, and centered on, the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.NORTH</tt>
       **     
       ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
       ** @see #ON_PIE_ARC ON_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see AnnotationLocation#NORTH NORTH
       **/
      public static final AnnotationLocation INSIDE_PIE_ARC = north;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just above, and horizontally centered on,
       ** the symbol used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation NORTH = north;


      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of and above,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation NORTHEAST =
        new AnnotationLocation(1, -1);

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of and above,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation
       **/
      public static final AnnotationLocation NORTHWEST =
        new AnnotationLocation(-1, -1);

     
      /**
       ** Specifies that a point's annotation (label) should
       ** be centered on the center-point of the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       **
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.CENTER</tt>
       **
       ** @see #OUTSIDE_PIE_ARC OUTSIDE_PIE_ARC
       ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see AnnotationLocation#CENTER CENTER
       **
       **/
      public static final AnnotationLocation ON_PIE_ARC = CENTER;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just outside, and centered on, the
       ** arc side of a pie slice.
       ** <p>
       **
       ** You can move a pie slice's annotation a specific number
       ** of pixels radially away from (or towards) the pie
       ** center by passing a positive (or negative) argument to
       ** the associated <tt>Point</tt>'s
       ** <tt>setAnnotationXShift</tt> method.
       **
       ** <p> This is pie-friendly synonym for, and when used
       ** with non-pie symbol types will behave exactly the same
       ** as, <tt>AnnotationLocation.SOUTH</tt>
       **
       ** @see #INSIDE_PIE_ARC INSIDE_PIE_ARC
       ** @see #ON_PIE_ARC ON_PIE_ARC
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see AnnotationLocation#SOUTH SOUTH
       **/
      public static final AnnotationLocation OUTSIDE_PIE_ARC = south;

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just below, and horizontally centered on,
       ** the symbol used to represent the point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTH = south;

     
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the right of and below,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTHEAST =
        new AnnotationLocation(1, 1);
      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of and below,
       ** the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation SOUTHWEST =
        new AnnotationLocation(-1, 1);

      /**
       ** Specifies that a point's annotation (label) should
       ** be positioned just to the left of, and vertically
       ** centered on, the symbol used to represent the
       ** point.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       **/
      public static final AnnotationLocation WEST = west;

     
     
      // these multiply the width and height of the annotation and
      // the symbol it is attached to in order to define the
      // center of the annotation (see equations in later code),
      // and thus the upper left corner anchoring point.
      private int heightMultiplier;
      private int widthMultiplier;
      private AnnotationLocation(int widthMultiplier,
                             int heightMultiplier) {
        validateMultipliers(widthMultiplier, heightMultiplier);
        this.widthMultiplier = widthMultiplier;
        this.heightMultiplier = heightMultiplier;
      }
      // retrieves a static location given its multipliers
      private static AnnotationLocation getAnnotationLocation(
         int widthMultiplier, int heightMultiplier) {
        final AnnotationLocation[][] locationMap = {
         {NORTHWEST, NORTH, NORTHEAST},
         {WEST, CENTER, EAST},
         {SOUTHWEST, SOUTH, SOUTHEAST}};
   // assumes both multiplier are -1, 0, or 1  
        AnnotationLocation result =
         locationMap[heightMultiplier+1][widthMultiplier+1];
       return result;               
      }
                                                  
      // Negative width or height "turn the symbol inside-out",
      // requiring a corresponding "reflection" of annotation
      // location (only needed for baseline-based bar symbols)
      static AnnotationLocation transform(AnnotationLocation a,
                                          int signWidth,
                                          int signHeight) {
        AnnotationLocation result = a;
        if (signWidth < 0 || signHeight < 0)
           result = getAnnotationLocation(
                signWidth*a.widthMultiplier,
                signHeight*a.heightMultiplier);

        return result;
      }
      // These define the alignment of the label within it's
      // containing 1 x 1 Grid. For example, if this
      // containing grid is to the left of the labeled
      // symbol (widthMultiplier==-1) the horizontal
      // alignment will be ALIGN_RIGHT, so as to bring the
      // contained label flush against the left edge of the
      // labeled symbol.
      HasHorizontalAlignment.HorizontalAlignmentConstant
            getHorizontalAlignment() {
         HasHorizontalAlignment.HorizontalAlignmentConstant result;
         if (widthMultiplier == -1)
            result = HasHorizontalAlignment.ALIGN_RIGHT;
         else if (widthMultiplier == 0)
            result = HasHorizontalAlignment.ALIGN_CENTER;
         else if (widthMultiplier == 1)
            result = HasHorizontalAlignment.ALIGN_LEFT;
         else
            throw new IllegalStateException(
               "Invalid widthMultiplier: " + widthMultiplier +
               " 1, 0, or -1 were expected.");
         return result;
      }

      /* Given the x-coordinate at the center of the symbol
       * that this annotation annotates, the annotation's
       * width, and the symbol's width, this method returns
       * the x-coordinate of the upper left corner of
       * this annotation.
       */
      int getUpperLeftX(double x, double w, double symbolW) {
         int result = (int) Math.round(x +
           (widthMultiplier * (w + symbolW) - w)/2.);
         return result;
      }

      /* analogous to getUpperLeftX, except for the y-coordinate */
      int getUpperLeftY(double y, double h, double symbolH) {
         int result = (int) Math.round(y +
           (heightMultiplier * (h + symbolH) - h)/2.);
         return result;
      }
      // analogous to getHorizontalAlignment 
      HasVerticalAlignment.VerticalAlignmentConstant
            getVerticalAlignment() {
         HasVerticalAlignment.VerticalAlignmentConstant result;
         if (heightMultiplier == -1)
            result = HasVerticalAlignment.ALIGN_BOTTOM;
         else if (heightMultiplier == 0)
            result = HasVerticalAlignment.ALIGN_MIDDLE;
         else if (heightMultiplier == 1)
            result = HasVerticalAlignment.ALIGN_TOP;
         else
            throw new IllegalStateException(
               "Invalid heightMultiplier: " + heightMultiplier +
               " -1, 0, or 1 were expected.");
         return result;
      }


       /*
        * This method returns the annotation location whose
        * "attachment point" keeps the annotation either
        * completely outside, centered on, or completely inside
        * (depending on if the heightMultiplier of this annotation
        * is 1, 0, or -1) the point on the pie's circumference
        * associated with the given angle.
        * <p>
        *
        * The use of heightMultiplier rather than widthMultiplier
        * is somewhat arbitrary, but was chosen so that the
        * NORTH, CENTER, and SOUTH annotation locations have the
        * same interpretation for a pie slice whose bisecting
        * radius points due south (due south is the default initial
        * pie slice orientation) and for a 1px x 1px BOX_CENTER
        * type symbol positioned at the due south position on the
        * pie's circumference. As the pie-slice-arc-bisection
        * point moves clockwise around the pie perimeter, the
        * attachment point (except for vertically-centered
        * annotations, which remain centered on the pie arc) also
        * moves clockwise, but in discrete jumps (e.g. from
        * NORTH, to NORTHEAST, to EAST, to SOUTHEAST, to SOUTH,
        * etc. for annotations inside the pie) so the annotation
        * remains appropriately attached to the center of the
        * slice's arc as the angle changes.
        *
        */
     AnnotationLocation decodePieLocation(double thetaMid) {
        // a sin or cos that is small enough so that the
        // associated angle is horizontal (for sines) or vertical
        // (for cosines) enough to warrent use of a "centered"
        // annotation location.
       final double LOOKS_VERTICAL_OR_HORIZONTAL_DELTA = 0.1;
       double sinTheta = Math.sin(thetaMid);
       double cosTheta = Math.cos(thetaMid);
       int pieTransformedWidthMultiplier = heightMultiplier *
          ((cosTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 :
          ((cosTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 : 0));
       int pieTransformedHeightMultiplier = heightMultiplier *
          ((sinTheta < -LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? 1 :
          ((sinTheta > LOOKS_VERTICAL_OR_HORIZONTAL_DELTA)? -1 : 0));
            
       return getAnnotationLocation(pieTransformedWidthMultiplier,
                                    pieTransformedHeightMultiplier);
        
     }
     
   } // end of class AnnotationLocation

  /**
   ** Represents an axis of the chart, for example, the x,
   ** y, or y2 axis. An axis consists of the axis itself,
   ** along with its tick marks, tick labels and gridlines.
   **
   ** @see XAxis XAxis
   ** @see YAxis YAxis
   ** @see Y2Axis Y2Axis
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **
   **
   **/
  public abstract class Axis {
     // holds info needed to render a single axis tick
     private class TickInfo {
        double position;  // along the tick axis in model units
        String tickLabel;
        Widget tickWidget;
        int widthUpperBound;  // upperbound on size of an enclosing
        int heightUpperBound; // 1 x 1 grid (used for centering, etc.)
        TickInfo(double position, String tickLabel, Widget tickWidget,
                 int widthUpperBound,
                 int heightUpperBound) {
           this.position = position;
           this.tickLabel = tickLabel;
           this.tickWidget = tickWidget;
           this.widthUpperBound = widthUpperBound;
           this.heightUpperBound = heightUpperBound;
        }
     } // end of class TickInfo
     protected class AxisLimits {
        double min; double max; // in user-defined model units
        AxisLimits(double min, double max) {
           this.min = min;
           this.max = max;
        }
        boolean equals(AxisLimits al) {
           boolean result =  (al.min == min && al.max == max);
           return result;
        }
     }

     // different initial curr, prev ==> "limits have changed" state
     private AxisLimits currentLimits = new AxisLimits(
        Double.MAX_VALUE, -Double.MAX_VALUE);
     private AxisLimits previousLimits = new AxisLimits(
        -Double.MAX_VALUE, Double.MAX_VALUE);
    
     private Widget axisLabel;
     protected int axisLabelThickness = GChart.NAI;
     private boolean hasGridlines = false;
     private int tickCount = DEFAULT_TICK_COUNT;
     private ArrayList tickInfo = new ArrayList();
// axes auto-scale whenever min or max are NaN.
     protected double axisMax = Double.NaN;
     protected double axisMin = Double.NaN;
// this symbol facilitates rendering of gridlines & axes
     protected Symbol gridSymbol;
     protected String tickLabelFontColor = DEFAULT_TICK_LABEL_FONT_COLOR;
// In CSS font-size pixels. These define the height of each
// character; our code relies on the rule of thumb that
// character width is approximately 3/5th this height to
// obtain a reasonably tight upper bound on tick label widths.
     protected int tickLabelFontSize = DEFAULT_TICK_LABEL_FONTSIZE;
     protected String tickLabelFontStyle = DEFAULT_TICK_LABEL_FONT_STYLE;
     protected String tickLabelFontWeight = DEFAULT_TICK_LABEL_FONT_WEIGHT;

     protected String tickLabelFormat = DEFAULT_TICK_LABEL_FORMAT;
     protected int tickLabelThickness = GChart.NAI;
     protected int ticksPerLabel = 1;
     protected int ticksPerGridline = 1;
     protected int tickLength = DEFAULT_TICK_LENGTH;
    
     // this symbol facilitates rendering of labeled tick-marks
     protected Symbol tickSymbol;
     protected int tickThickness = DEFAULT_TICK_THICKNESS;
     protected WidgetCursor widgetCursor = new WidgetCursor();

        
     // is axis itself visible (has no impact ticks or their labels)
     boolean axisVisible = true;
    
     WidgetCursor getWidgetCursor() { return widgetCursor;}
     /**
      * Adds a tick on this axis at the specified position.
      * Note that explicitly adding a single tick via this method
      * will eliminate any implicitly generated ticks associated with the
      * <tt>setTickCount</tt> method.
      * <p>
      * The label associated with this tick will be generated by
      * applying the format specified via <tt>setTickLabelFormat</tt>
      * to the specified position.
      * <p>
      * This is a convenience method equivalent to
      * <tt>addTick(tickPosition, null, GChart.NAI,
      * GChart.NAI)</tt>. See {@link #addTick(double,String,int,int)
      * addTick(tickPosition,tickLabel,widthUpperBound,heightUpperBound)}
      * for details.
      *
      * @param tickPosition the position, in model units,
      *   along this axis at which this tick is displayed.
      *   For example, if the axis range goes from 0 to 100,
      *   a tick at position 50 would appear in the middle of
      *   the axis.
      *
      * @see #clearTicks clearTicks
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      * @see #setTickCount setTickCount
      * @see #setTickLabelFormat setTickLabelFormat
      * @see #setTickLabelFontStyle setTickLabelFontStyle
      * @see #setTickLabelFontColor setTickLabelFontColor
      * @see #setTickLabelFontWeight setTickLabelFontWeight
      * @see #setTickLabelFontSize setTickLabelFontSize
      *
      */
     public void addTick(double tickPosition) {
        addTick(tickPosition, (String) null)
     }
     /**
      * Adds a tick at the specified position with the specified
      * label on this axis, whose width and height are within
      * the specified upper-bounds.
      *
      * <p>
      * Note that explicitly adding a single tick via this method
      * will eliminate any auto-generated ticks associated with the
      * <tt>setTickCount</tt> method.
      *
      * <p>
      * Use this method to specify unusually spaced
      * tick marks with labels that do not directly
      * reflect the position (for example, for a logarithmic axis,
      * or for a bar chart with special keyword-type labels, or
      * a time axis that places date and time on two separate lines).
      *
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      *  @param tickLabel the label for this tick.  HTML is
      *  supported in tick labels, but it must be prefixed by
      *  <tt>&lt;html&gt</tt>.  See the {@link
      *  Curve.Point#setAnnotationText(String,int,int)
      *  setAnnotationText} method for more information.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      *  get GChart to estimate this width for you. See the
      *  <tt>setAnnotationText</tt> method for more information.
      *
      *  @param heightUpperBound an upper bound on the height of
      *  the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      *  get GChart to estimate this height for you. See the
      *  <tt>setAnnotationText</tt> method for more information.
      *
      * @see #clearTicks clearTicks
      * @see #addTick(double) addTick(double)
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      * @see #setTickCount setTickCount
      * @see #setTickLabelFormat setTickLabelFormat
      * @see #setTickLabelFontSize setTickLabelFontSize
      * @see #setTickLabelFontStyle setTickLabelFontStyle
      * @see #setTickLabelFontColor setTickLabelFontColor
      * @see #setTickLabelFontWeight setTickLabelFontWeight
      * @see Curve.Point#setAnnotationText(String,int,int)
      *      setAnnotationText
      * @see Curve.Point#setAnnotationWidget setAnnotationWidget
      *     
      */
     public void addTick(double tickPosition, String tickLabel,
                         int widthUpperBound,
                         int heightUpperBound) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
// position of plot area can depend on size of tick labels
       plotPanel.plotAreaNeedsUpdate();
       tickCount = 0; // eliminates any auto-generated ticks  
       tickInfo.add(new TickInfo(tickPosition, tickLabel, null,
                                 widthUpperBound,
                                 heightUpperBound));
     }

     /**
      * Adds a tick at the specified position with the specified
      * label on this axis.
      * <p>
      *
      * This is a convenience method equivalent to
      * <tt>addTick(tickPosition, tickLabel, GChart.NAI,
      * GChart.NAI)</tt>. Most applications can usually just
      * use this convenience method. See {@link #addTick(double,String,int,int)
      * addTick(tickPosition,tickLabel,
      * widthUpperBound,heightUpperBound)} for the fine print.
      *
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *  
      * @param tickLabel the plain text or
      * (<tt>&lt;html&gt</tt>-prefixed) HTML defining the tick's
      * label.
      *
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget) addTick(double,Widget)
      *
      */
     public void addTick(double tickPosition,
                         String tickLabel) {
          addTick(tickPosition, tickLabel, GChart.NAI, GChart.NAI);
     }
    
     /**
      *  Adds a widget-defined tick label at the specified
      *  position, whose width and height are within
      *  the specified upper-bounds.
      *
      *
      *  <p>
      **
      ** This method is similar to
      ** <tt>addTick(double,String,int,int)</tt> except that it
      ** uses a widget, rather than a string, to define the
      ** tick's label. Although the string-based method is faster
      ** on first chart rendering, and uses less memory, the
      ** widget-based method allows you to change the label
      ** independently of the chart--potentially bypassing (or
      ** speeding up) expensive chart updates later on.
      **
      ** <p>
      **
      ** You might use a widget-based tick label to pop up a
      ** dialog that allows the user to edit the parameters
      ** defining the axis (min, max, etc.) whenever they click
      ** on one of the tick labels on that axis, to define
      ** hovertext that appears when the user mouses over
      ** a tick label, to use images for your tick labels, etc.
      **
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      *  @param tickWidget the label for this tick, as defined
      *  by any GWT Widget.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the widget, in pixels. If this and the next
      *  parameter are omitted, GChart will use
      *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
      * 
      *  @param heightUpperBound an upper bound on the height of
      *  the widget, in pixels. If this and the previous
      *  parameter are omitted, GChart will use <tt>
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
      *
      *  @see #addTick(double,Widget) addTick(double,Widget)
      *  @see #addTick(double,String,int,int) addTick(double,String,int,int)
      *  @see Curve.Point#setAnnotationWidget setAnnotationWidget
      *  @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      *  @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      **/
     public void addTick(double tickPosition,
                         Widget tickWidget,
                         int widthUpperBound,
                         int heightUpperBound) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       plotPanel.plotAreaNeedsUpdate();
       // accept "Not an Integer" (because text-based addTick does)
       if (widthUpperBound == GChart.NAI)
          widthUpperBound = DEFAULT_WIDGET_WIDTH_UPPERBOUND;
       if (heightUpperBound == GChart.NAI)
          heightUpperBound = DEFAULT_WIDGET_HEIGHT_UPPERBOUND;
       tickCount = 0; // eliminates any auto-generated ticks  
       tickInfo.add(new TickInfo(tickPosition, null, tickWidget,
                                 widthUpperBound,
                                 heightUpperBound));
     }

     /**
      *  Adds a Widget-defined tick label at the specified
      *  position. Convenience method equivalent to
      *  <tt>addTick(tickPosition, tickWidget,
      *  DEFAULT_WIDGET_WIDTH_UPPERBOUND,
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>.
      * 
      * @param tickPosition the position, in model units, along
      *   this axis at which the tick is displayed.
      *   For example, if the axis range goes from 0 to 1,
      *   a tick at position 0.5 would appear in the middle of
      *   the axis.
      *  
      * @param tickWidget the label for this tick, as defined
      *  by any GWT Widget.
      * 
      * @see #addTick(double,Widget,int,int)
      * addTick(double,Widget,int,int)
      * 
      */
     public void addTick(double tickPosition,
                         Widget tickWidget) {
        addTick(tickPosition, tickWidget,
                DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
     }
     /**
      *
      * Removes all ticks from this axis. Specifically,
      * erases any ticks that were explicitly specified via
      * <tt>addTick</tt>, and also sets the tick count to 0.
      * <p>
      *
      * @see #setTickCount setTickCount
      * @see #addTick(double) addTick(double)
      * @see #addTick(double,String) addTick(double,String)
      * @see #addTick(double,String,int,int) addTick(double,String,int,int)
      * @see #addTick(double,Widget) addTick(double,Widget)
      * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      *
      */
     public void clearTicks() {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        plotPanel.plotAreaNeedsUpdate();
        tickCount = 0;
        tickInfo.clear();
     }
     // these are used in formatting tick positions into tick labels:
     private NumberFormat numberFormat =
        NumberFormat.getFormat(DEFAULT_TICK_LABEL_FORMAT);
     private DateTimeFormat dateFormat =
        DateTimeFormat.getShortDateTimeFormat();
     private final int NUMBER_FORMAT_TYPE = 0;
     private final int DATE_FORMAT_TYPE = 1;
     private final int LOG10INVERSE_FORMAT_TYPE = 2;
     private final int LOG2INVERSE_FORMAT_TYPE = 3;
     private int tickLabelFormatType = NUMBER_FORMAT_TYPE;
     /**
     *
     * Applies this axis' tick label format to format a given value.
     *
     * @return the value formated as per this axis' currently specified
     * tick label format.
     *
     * @see  #setTickLabelFormat(String) setTickLabelFormat
     *
     */
     public String formatNumberAsTickLabel(double value) {
       String result = null;
       switch (tickLabelFormatType) {
         case DATE_FORMAT_TYPE:
           Date transDate = new Date((long) value);
           result = dateFormat.format(transDate);
           break;
         case LOG10INVERSE_FORMAT_TYPE:
           value = Math.pow(10., value);
           result = numberFormat.format(value);
           break;
         case LOG2INVERSE_FORMAT_TYPE:
           value = Math.pow(2., value);
           result = numberFormat.format(value);
           break;
         default
           result = numberFormat.format(value);
           break;
       }
      
       return result;
    }

     /** Returns the previously specified label of this axis.
      **
      ** @return the Widget used as the label of this axis
      **
      ** @see #setAxisLabel setAxisLabel
      **
      */
     public Widget getAxisLabel() {
       return axisLabel;
     }
    
     /** Returns the thickness of the axis-label-holding region
      ** adjacent to the region allocated for this axis' tick labels.
      ** <p>
      **
      ** Note that if the axis label is <tt>null</tt> (the
      ** default) then this method always returns 0, since
      ** in that case no rectangular region will be allocated
      ** for the axis label.
      ** <p>
      **
      ** @return the thickness of the axis-label-holding
      ** region, in pixels.
      **
      ** @see #setAxisLabelThickness setAxisLabelThickness
      **
      */
     public int getAxisLabelThickness() {
        int result = 0;
// Base class implementation is for y axes (x-axis will override).
        final int EXTRA_CHARWIDTH = 2; // 1-char padding on each side
        final int DEF_CHARWIDTH = 1; // when widget has no text
        if (null == getAxisLabel())
           result = 0;
        else if (GChart.NAI != axisLabelThickness)
           result = axisLabelThickness;
        else if (getAxisLabel() instanceof HasHTML) {
           int charWidth = htmlWidth(
              ((HasHTML) (getAxisLabel())).getHTML());
           result = (int) Math.round((charWidth + EXTRA_CHARWIDTH) *
                      getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        }
        else if (getAxisLabel() instanceof HasText) {
           String text = ((HasText) (getAxisLabel())).getText();
           result = (int) Math.round((EXTRA_CHARWIDTH +
                      ((null==text)?0:text.length())) *
                      getTickLabelFontSize() *
                      TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        }
        else // non-text widget. Not a clue, just use def width
           result = (int) Math.round(
                    (DEF_CHARWIDTH + EXTRA_CHARWIDTH) *
                     getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        return result;
     }
     /**
      ** Returns the maximum value displayed on this axis.
      ** If the explicitly specified maximum value is
      ** undefined (<tt>Double.NaN</tt>) the maximum value returned
      ** by this function is calculated as the maximum of
      ** all of the values either displayed on this axis via
      ** points on a curve, or explicitly specified via tick
      ** positions.
      **
      ** @return maximum value visible on this axis, in
      ** "model units" (arbitrary, application-specific,
      ** units)
      **
      ** @see #setAxisMax setAxisMax
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      **/
     public double getAxisMax() {
          
        if (!(axisMax!=axisMax)) { // x!=x is a faster isNaN
           return axisMax;
        }
        else if (tickCount > 0) {
          return getDataMax();
        }
        else {
           return Math.max(getDataMax(), getTickMax());          
        }
     }
     /**
      **
      ** Returns the minimum value displayed on this axis.
      ** If the minimum value is undefined (<tt>Double.NaN</tt>) the
      ** minimum value returned by this function is the
      ** minimum of all of the values either displayed on
      ** this axis via points on a curve, or explicitly specified
      ** via tick positions.
      **
      ** @return minimum value visible on this axis, in
      ** "model units" (arbitrary, application-specific,
      ** units)
      **
      ** @see #setAxisMin setAxisMin
      **/
     public double getAxisMin() {
        if (!(axisMin!=axisMin)) { // x!=x is a faster isNaN
           return axisMin; // explicitly set
        }
        else if (tickCount > 0) {
           return getDataMin();
        }
        else
           return Math.min(getDataMin(), getTickMin());          
        }
     }

   /** Is axis line visible on the chart? Note that
    ** this property only determines the visibility of the axis line
    ** itself. It does not control the visibility of the
    ** tick marks or tick labels along this axis.
    ** <p>
    **
    ** @return true if the axis line is visible, false otherwise.
    **
    ** @see #setAxisVisible setAxisVisible
    **
    **/
     public boolean getAxisVisible() {
       return axisVisible;
     }


    
     /** Returns the maximum data value associated with values
     ** represented on this axis. For example, for the left
     ** y-axis, this would be the largest y-value of all points
     ** contained in curves that are displayed on the left y-axis.
     **
     ** @return the maximum value associated with values
     **   mapped onto this axis.
     **
     ** @see #getDataMin getDataMin
     ** @see #getAxisMax getAxisMax
     ** @see #getAxisMin getAxisMin
     **
      */
    public abstract double getDataMax();
     /** Returns the minimum data value associated with values
     ** represented on this axis. For example, for the left
     ** y-axis, this would be the smallest y-value of all points
     ** contained in curves that are displayed on the left y-axis.
     **
     ** @return the minumum value associated with values
     **   mapped onto this axis.
     **
     ** @see #getDataMax getDataMax
     ** @see #getAxisMax getAxisMax
     ** @see #getAxisMin getAxisMax
     **
      */
    public abstract double getDataMin();
    
     /** Returns the gridline setting previously made with
      ** <tt>setHasGridlines</tt>.
      **
      ** @return true if gridlines have been enabled, false if not.
      **
      ** @see #setHasGridlines setHasGridlines
      **
      **/
     public boolean getHasGridlines() {
        return hasGridlines;
     }
     /**
      ** Returns the number of ticks on this axis.
      **
      ** @return the number of ticks on this axis.
      **
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #clearTicks clearTicks
      **
      **/
     public int getTickCount() {
        if (tickCount == 0)
           return tickInfo.size();
        else
           return tickCount;
     }
     /**
      ** Returns the CSS font-weight specification to be used
      ** by this axis' tick labels.
      **
      ** @return font-weight of this axis' tick labels
      **
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      **/
     public String getTickLabelFontWeight() {
        return tickLabelFontWeight;
     }
     /**
      ** Returns the color of the font used to display the
      **    text of the tick labels on this axis.
      **  
      **  
      ** @return CSS color string defining the color of the text of
      **    the tick labels for this axis.
      **
      ** @see #setTickLabelFontColor setTickLabelFontColor
      **
      ** @see #DEFAULT_TICK_LABEL_FONT_COLOR DEFAULT_TICK_LABEL_FONT_COLOR
      **
      **
      **
      **/
     public String getTickLabelFontColor() {
        return tickLabelFontColor;
     }

     /**
      ** Returns the font-style of the font used to render tick
      ** labels on this axis (typically either "italic" or
      ** "normal")
      **
      ** @return the CSS font-style in which tick labels of this axis
      **   are rendered.
      **
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      **/
     public String getTickLabelFontStyle() {
        return tickLabelFontStyle;
     }
     /** Returns the font size, in pixels, used for tick labels
      ** on this axis.
      **
      ** @return the tick label font size in pixels
      **
      ** @see #setTickLabelFontSize setTickLabelFontSize
      **/
     public int getTickLabelFontSize() {
        return tickLabelFontSize;
     }

     /**
     ** Returns the tick label numeric format string for this
     ** axis.
     **
     ** @return numeric format used to generate tick labels.
     **
     ** @see #setTickLabelFormat setTickLabelFormat
     **
     **/
    public String getTickLabelFormat() {
       return tickLabelFormat;
    }
    /** Returns the thickness of the band adjacent to
     ** this axis that GChart will
     ** allocate to hold this axis' tick labels.
     ** <p>
     **
     ** @return width of band, in pixels, GChart will reserve
     **   for this axis' tick labels.
     **  
     ** @see #setTickLabelThickness setTickLabelThickness
     **
     **/
    public int getTickLabelThickness() {
      int maxLength = 0;
      int result;
      if (tickLabelThickness != GChart.NAI)
        result = tickLabelThickness;
      else { // use an heuristic to estimate thickness           
       maybePopulateTicks();
       for (int i=0; i < tickInfo.size(); i++) {
         String tt = getTickText(i);
         if (null != tt)
            maxLength = Math.max(maxLength,
                                 Annotation.getNumberOfCharsWide(tt));
       }
       result = (int) Math.round(maxLength * tickLabelFontSize *
                TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
      }
      return result;
    }


    /**
     ** Returns the ratio of the number of ticks to the number of
     ** ticks that have an associated gridline.
     **
     ** @return number of ticks per gridline for this axis
     **
     ** @see #setTicksPerGridline setTicksPerGridline
     **
     **/
    public int getTicksPerGridline() {
       return ticksPerGridline;
    }
    /**
     ** Returns the ratio of the number of ticks to the number of
     ** labeled ticks.
     **
     ** @return number of ticks per label.
     **
     ** @see #setTicksPerLabel setTicksPerLabel
     **
     **/
    public int getTicksPerLabel() {
      return ticksPerLabel;
    }

     /**
     * Returns the length of ticks for this axis.
     *
     * @return the length of this axis' ticks, in pixels.
     *
     * @see #setTickLength setTickLength
     */
    public int getTickLength() {
       return tickLength;
    }

    /**
     * Returns the thickness of ticks for this axis.
     *
     * @return the thickness of this axis' ticks, in pixels.
     *
     * @see #setTickThickness setTickThickness
     * @see #getTickLength getTickLength
     */
    public int getTickThickness() {
       return tickThickness;
    }

     /** Specifies the label of this axis.
      ** <p>
      **
      ** This label will be positioned just outside of, and
      ** centered lengthwise on, the region adjacent to
      ** this axis that GChart reserves for this axis' tick labels.
      **
      ** @param axisLabel a Widget to use as the label of this axis.
      **
      ** @see #getAxisLabel getAxisLabel
      ** @see #setTickLabelThickness setTickLabelThickness
      ** @see #setAxisLabelThickness setAxisLabelThickness
      **
      */
    
     public void setAxisLabel(Widget axisLabel) {
        this.axisLabel = axisLabel;
        GChart.this.chartDecorationsChanged = true;
     }
    
     /**
      * Convenience method equivalent to
      * <tt>setAxisLabel(new HTML(html))</tt>
      *
      * @param html HTML text used to define the axis label
      *
      * @see #setAxisLabel(Widget) setAxisLabel(Widget)
      */
      public void setAxisLabel(String html) {
       setAxisLabel(new HTML(html));
      }

     /** Sets the thickness of the axis-label-holding region
      ** adjacent to the region allocated for tick labels.<p>
      **
      ** The axis label widget will be centered in this region.
      ** Choose a thickness large enough to hold the largest
      ** font size you want users to be able to zoom up to
      ** without the axis label spilling over into
      ** adjacent regions.
      ** <p>
      **
      ** If the axis label thickness is <tt>GChart.NAI</tt> (the
      ** default), and the widget defining the axis label
      ** implements <tt>HasHTML</tt> (or <tt>HasText</tt>) then
      ** GChart uses a thickness based on the estimated number of
      ** non-tag characters in the first <tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt>
      ** delimited line for y-axis labels, and based on the
      ** estimated number of (<tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt> delimited)
      ** text lines for x-axis labels.<p>
      **
      ** Note that if the axis label is <tt>null</tt> (its
      ** default setting) then no space is allocated for the axis
      ** label, regardless of this thickness setting.
      ** <p>
      **
      ** @param thickness the thickness of the axis-label-holding
      ** region, in pixels, or <tt>GChart.NAI</tt> to use
      ** GChart's character-based default thickness estimates.
      **
      ** @see #getAxisLabelThickness getAxisLabelThickness
      ** @see #setAxisLabel setAxisLabel
      */
     public void setAxisLabelThickness(int thickness) {
       axisLabelThickness = thickness;
       GChart.this.chartDecorationsChanged = true;
     }

     /**
      ** Specifies the maximum value visible on this axis.
      ** <p>
      **
      ** Points with associated coordinates greater than this
      ** value will be clipped unless the chart's
      ** <tt>showOffChartPoints</tt> property is <tt>true</tt>.
      **
      ** <p>
      **
      ** If <tt>Double.NaN</tt> is specified, this maximum
      ** is auto-determined as described in <tt>getAxisMax</tt>.
      **
      ** <p> <i>Performance tip:</i> Using auto-determined axis
      ** limits (via <tt>Double.NaN</tt>) forces GChart to
      ** re-render the axis every time you call
      ** <tt>GChart.update</tt>.  When your axis limits don't
      ** change between updates, these (often expensive)
      ** re-renderings can be avoided by using explicitly
      ** specified axis limits on every axis.  <p>
      **
      ** @param max maximum value visible on this axis, in "model units"
      ** (arbitrary, application-specific, units) or <tt>Double.NaN</tt>
      ** (the default value) to use an auto-determined maximum.
      **
      ** @see #getAxisMax getAxisMax
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      ** @see GChart#setShowOffChartPoints setShowOffChartPoints
      **
      **/
     public void setAxisMax(double max) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
// max can change axis label width ==> changes position of plot area
        plotPanel.plotAreaNeedsUpdate();
        this.axisMax = max;
     }
     /**
      ** Specifies the minimum value of this axis.
      ** <p>
      **
      ** Points with associated coordinates less than this
      ** value will be clipped unless the chart's
      ** <tt>showOffChartPoints</tt> property is <tt>true</tt>.
      ** <p>
      **
      ** If <tt>Double.NaN</tt> is specified, this minimum
      ** is auto-determined as described in <tt>getAxisMin</tt>.
      **
      ** <p> <i>Performance tip:</i> Using auto-determined axis
      ** limits (via <tt>Double.NaN</tt>) forces GChart to
      ** re-render the axis every time you call
      ** <tt>GChart.update</tt>.  When your axis limits don't
      ** change between updates, these (often expensive)
      ** re-renderings can be avoided by using explicitly
      ** specified axis limits on every axis.  <p>
      **
      ** @param min minumum value visible on this axis, in "model units"
      ** (arbitrary, application-specific, units), or Double.NaN
      ** (the default) to use an auto-determined minimum.
      **
      ** @see #getAxisMin getAxisMin
      ** @see #getDataMin getDataMin
      ** @see #getDataMax getDataMax
      **
      **/
     public void setAxisMin(double min) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
// min can change axis label width ==> changes position of plot area
        plotPanel.plotAreaNeedsUpdate();
        this.axisMin = min;
     }

  /**
   ** Defines if this axis is visible. Note that
   ** this property only defines the visibility of the axis line
   ** itself, it does not control the visibility of
   ** tick marks or tick labels associated with the axis.
   **
   ** <p>
   ** <i>Tip:</i>Tick marks can be made invisible by using
   ** <tt>setTickThickness</tt> to set the tick thickness
   ** to 0. Tick labels can be made invisible by using
   ** <tt>setTickLabelFontColor</tt> to set the tick label
   ** color to the chart's background color.
   ** <p>
   **
   ** @param axisVisible false to hide axis, true to show it.
   **
   ** @see #setTickThickness setTickThickness
   ** @see #setTickLabelFontColor setTickLabelFontColor
   ** @see #getAxisVisible getAxisVisible
   **/
   public void setAxisVisible(boolean axisVisible) {
      plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
      this.axisVisible = axisVisible;
   }
  
    
     /**
      ** Specifies if this axis should have gridlines. When an
      ** axis has gridlines, tick marks with indexes <tt>0, N,
      ** 2*N,...</tt> where <tt>N</tt> is the value of this axis'
      ** <tt>ticksPerGridline</tt> property, are in effect
      ** extended across the entire chart.
      **
      ** @param hasGridlines true to display gridlines,
      ** false (the default) to not display them.
      **
      ** @see #getHasGridlines getHasGridlines
      ** @see #setTicksPerGridline setTicksPerGridline
      **
      **/
     public void setHasGridlines(boolean hasGridlines) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        this.hasGridlines = hasGridlines;
     }
     /** Sets the number of ticks to be placed on this axis. The
      ** default tick count is 10. Ticks are always evenly
      ** spaced across the entire axis, unless explicitly
      ** specified via <tt>addTick</tt>.
      ** <p>
      **
      ** Note that setting the tick count overrides (erases)
      ** any ticks explicitly specified via <tt>addTick</tt>.
      **
      ** @param tickCount the number of ticks for this axis.
      **
      ** @see #getTickCount getTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double, String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      **
      **/
     public void setTickCount(int tickCount) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        plotPanel.plotAreaNeedsUpdate();
        tickInfo.clear();    // eliminate user specified ticks
        this.tickCount = tickCount;
     }

    
     /**
      ** Specifies the weight of the font used in this axis' tick
      ** labels.
      **
      ** @param cssWeight the weight of the font, such as bold,
      **    normal, light, 100, 200, ... 900, for tick labels.
      **
      ** @see #getTickLabelFontWeight getTickLabelFontWeight 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #DEFAULT_TICK_LABEL_FONT_WEIGHT DEFAULT_TICK_LABEL_FONT_WEIGHT
      **/
     public void setTickLabelFontWeight(String cssWeight) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        tickLabelFontWeight = cssWeight;
     }
     /**
      ** Specifies the color of the font used to render tick labels
      ** for this axis.
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display this
      **    axes' tick labels, in standard CSS format.
      **
      ** @see #getTickLabelFontColor getTickLabelFontColor 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see #setTickLabelFontSize setTickLabelFontSize
      **/
     public void setTickLabelFontColor(String cssColor) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        tickLabelFontColor = cssColor;
     }

     /**
      ** Specifies the CSS font-style of this
      ** axis' tick labels.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getTickLabelFontStyle getTickLabelFontStyle 
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see #setTickLabelFontSize setTickLabelFontSize
      ** @see #DEFAULT_TICK_LABEL_FONT_STYLE
      ** DEFAULT_TICK_LABEL_FONT_STYLE
      **/
     public void setTickLabelFontStyle(String cssStyle) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        tickLabelFontStyle = cssStyle;
     }

     /**
      ** Sets the tick label font size for tick labels on this
      ** axis, in pixels.
      **
      ** @param tickLabelFontSize the font size of tick labels
      **   displayed on this axis.
      **
      ** @see #getTickLabelFontSize getTickLabelFontSize
      ** @see #setTickLabelFormat setTickLabelFormat
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      ** @see #setTickLabelFontStyle setTickLabelFontStyle
      ** @see #setTickLabelFontColor setTickLabelFontColor
      ** @see #setTickLabelFontWeight setTickLabelFontWeight
      ** @see GChart#DEFAULT_TICK_LABEL_FONTSIZE DEFAULT_TICK_LABEL_FONTSIZE
      **
      **/

     public void setTickLabelFontSize(int tickLabelFontSize) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        GChart.this.chartDecorationsChanged = true;
        plotPanel.plotAreaNeedsUpdate();
        this.tickLabelFontSize = tickLabelFontSize;
     }

     /**
     * Specifies a format string to be used in
     * converting the numeric values associated with each
     * tick on this axis into tick labels.  This string must
     * follow the conventions of the number format patterns
     * used by the GWT <a
     * href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/NumberFormat.html">
     * NumberFormat</a> class, <i>with three
     * exceptions:</i>
     * <p>
     * <ol>
     *
     *  <li><i>Log10 inverse prefix</i>: If the string begins
     *  with the prefix <tt>=10^</tt> the value is replaced with
     *  <tt>pow(10.,value)</tt> and the so-transformed value is
     *  then formatted using the part of the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <tt>NumberFormat</tt> pattern (e.g. "##.##").
     *  <p>
     *  For an example of how to use this prefix to create a
     *  semi-log plot, see <a
     *  href="package-summary.html#GChartExample04">the
     *  Chart Gallery's GChartExample04</a>.
     *  <p>
     *
     *  <li><i>Log2 inverse prefix</i>: If the string begins with
     *  the prefix <tt>=2^</tt> the value is replaced with
     *  <tt>pow(2.,value)</tt> and the so-transformed value is
     *  then formatted using the part of the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <tt>NumberFormat</tt> pattern.
     *  <p>
    
     *  <li><i>Date casting prefix</i>: If the string begins with
     *  the prefix <tt>=(Date)</tt> the value is replaced with
     *  <tt>new Date((long) value)</tt> and the so-transformed
     *  value is then formatted using the format string that
     *  comes after this prefix, which must be a valid GWT
     *  <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/DateTimeFormat.html">
     *  DateTimeFormat</a>  pattern (e.g. "yyyy-MM-dd&nbsp;HH:mm").
     *  For the special case format string of <tt>"=(Date)"</tt>
     *  (just the date casting prefix) GChart uses the
     *  <tt>DateTimeFormat</tt> returned by the
     *  <tt>DateTimeFormat.getShortDateTimeFormat</tt> method.  <p>
    
     *  Note that the values associated with this Axis must
     *  represent the number of milliseconds since January 1,
     *  1970 (in the GMT time zone) whenever this date
     *  casting prefix is used.  <p>
     *
     * 
     *  For example, if the x-axis tick label format were
     *  "=(Date)MMM-dd-yyyy HH", then, for a tick located at the
     *  x position of 0, the tick label would be "Jan-01-1970 00"
     *  (on a client in the GMT time zone) and for a tick located
     *  at the x position of 25*60*60*1000 (one day + one hour,
     *  in milliseconds) the tick label would be "Jan-02-1970 01"
     *  (again, on a GMT-based client). Results would be
     *  shifted appropriately on clients in different time zones.
     *  <p>
     *
     *  Note that if your chart is based on absolute, GMT-based,
     *  millisecond times then date labels will change when your
     *  chart is displayed on clients in different time zones.
     *  Sometimes, this is what you want. To keep the date labels
     *  the same in all time zones, convert date labels into Java
     *  <tt>Date</tt> objects in your client-side code, then use
     *  the <tt>Date.getTime</tt> method, also in your
     *  client-side code, to convert those dates into the
     *  millisecond values GChart requires.  The <a
     *  href="package-summary.html#GChartExample12"> Chart
     *  Gallery's GChartExample12</a> illustrates how to use this
     *  second approach to produce a time series chart whose
     *  date-time labels are the same in all time zones.
     * 
     *  <p>
    
     *  <blockquote><small>
    
     *  Ben Martin describes an alternative (and more flexible)
     *  approach to formatting time series tick labels in his <a
     *  href="http://www.linux.com/feature/132854">GChart
     *  tutorial</a>. Ben's article, along with Malcolm Gorman's
     *  related <a
     *  href="http://groups.google.com/group/Google-Web-Toolkit/msg/6125ce39fd2339ac">
     *  GWT forum post</a> were the origin of this date
     *  casting prefix. Thanks! </small></blockquote>
     *   
     * </ol>
     * <p>
     *
     *
     * <p> Though HTML text is not supported in the tick label
     * format string, you can change the size, weight, style, and
     * color of tick label text via the
     * <tt>setTickLabelFont*</tt> family of methods. You
     * <i>can</i> use HTML in tick labels (e.g. for a multi-line
     * x-axis label) but but only if you define each tick label
     * explicitly using the <tt>addTick</tt> method.
     *
     * @param format an appropriately prefixed
     *   GWT <tt>NumberFormat</tt> compatible or
     *   GWT <tt>DateTimeFormat</tt> compatible format string that
     *   defines how to convert tick values into tick labels.
     *
     * @see #setTickCount setTickCount
     * @see #addTick(double) addTick(double)
     * @see #addTick(double,String) addTick(double,String)
     * @see #addTick(double,String,int,int) addTick(double,String,int,int)
     * @see #addTick(double,Widget) addTick(double,Widget)
     * @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
     * @see #setTickLabelFontSize setTickLabelFontSize
     * @see #setTickLabelFontStyle setTickLabelFontStyle
     * @see #setTickLabelFontColor setTickLabelFontColor
     * @see #setTickLabelFontWeight setTickLabelFontWeight
     * @see #getTickLabelFormat getTickLabelFormat
     */
     public void setTickLabelFormat(String format) {
       // interpret prefixes and create an appropriate formatter
       if (!tickLabelFormat.equals(format)) {
         GChart.this.chartDecorationsChanged = true;
         plotPanel.plotAreaNeedsUpdate();
         if (format.startsWith("=(Date)")) {
           String transFormat = format.substring("=(Date)".length());
           if (transFormat.equals("")) // so "=(Date)" works
             dateFormat = DateTimeFormat.getShortDateTimeFormat();
           else // e.g. "=(Date)mm/dd/yy hh:mm"
             dateFormat = DateTimeFormat.getFormat(transFormat);
           tickLabelFormatType = DATE_FORMAT_TYPE;
         }
         else if (format.startsWith("=10^")) {
           String transFormat = format.substring("=10^".length());
           numberFormat = NumberFormat.getFormat(transFormat);
           tickLabelFormatType = LOG10INVERSE_FORMAT_TYPE;
         }
         else if (format.startsWith("=2^")) {
           String transFormat = format.substring("=2^".length());
           numberFormat = NumberFormat.getFormat(transFormat);
           tickLabelFormatType = LOG2INVERSE_FORMAT_TYPE;
         }
         else {
           numberFormat = NumberFormat.getFormat(format);
           tickLabelFormatType = NUMBER_FORMAT_TYPE;
         }
       }
       // remember original format (for use with the getter)
       tickLabelFormat = format;
     }

     /** Specifies the thickness of the region adjacent to
      ** this axis that GChart will reserve for purposes of
      ** holding this axis' tick labels.  <p>
      ** <p>
      **
      ** For vertical axes, this represents the width of the
      ** widest tick label, for horizontal axes, this represents
      ** the height of highest tick label.
      ** <p>
      **
      **
      ** By default, this property has the special "undefined"
      ** value <tt>GChart.NAI</tt>. With this value, the
      ** companion method <tt>getTickLabelThickness</tt> uses an
      ** HTML-based heuristic to estimate the thickness.
      **
      **
      ** @see #getTickLabelThickness getTickLabelThickness
      ** @see #getTickLabelFontSize getTickLabelFontSize
      ** @see #setAxisLabel setAxisLabel
      ** @see GChart#NAI NAI
      **
      **/
     public void setTickLabelThickness(int tickLabelThickness) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       GChart.this.chartDecorationsChanged = true;
       plotPanel.plotAreaNeedsUpdate();
       this.tickLabelThickness = tickLabelThickness;
     }
     /** Specifies the ratio of the number of tick marks on the
      ** axis, to the number of gridlines on the axis.
      ** <p>
      **
      ** For example, with the default value of 1, every tick has
      ** an associated gridline, whereas with a
      ** <tt>ticksPerGridline</tt> setting of 2, only the first,
      ** third, fifth, etc. ticks have gridlines. 
      **
      ** <p>
      **
      ** This setting only has an impact when the axis' gridlines
      ** are turned on, that is, when this axis'
      ** <tt>getHasGridlines</tt> method returns true.
      **     
      ** @see #setHasGridlines setHasGridlines
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      **
      ** @param ticksPerGridline the number of ticks on this
      **   axis per "gridline-extended" tick.
      **
      **/
     public void setTicksPerGridline(int ticksPerGridline) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       if (ticksPerGridline <= 0)
         throw new IllegalArgumentException(
           "ticksPerGridline must be > 0");
       this.ticksPerGridline = ticksPerGridline;
     }
     /** Specifies the ratio of the number of tick marks on the
      ** axis, to the number of labeled tick marks on the axis.
      ** <p>
      **
      ** For example, with the default value of 1, every tick is
      ** labeled, whereas with a <tt>ticksPerLabel</tt> setting
      ** of 2, only the first, third, fifth, etc. ticks are
      ** labeled.
      **
      ** <p>
      **
      ** This setting is only used when tick labels
      ** are specified implicitly via <tt>setTickCount</tt>. It
      ** is ignored when tick positions and their labels are
      ** explicitly specified via <tt>addTick</tt>.
      **
      ** @see #setTickCount setTickCount
      ** @see #addTick(double) addTick(double)
      ** @see #addTick(double,String) addTick(double,String)
      ** @see #addTick(double,String,int,int) addTick(double,String,int,int)
      ** @see #addTick(double,Widget) addTick(double,Widget)
      ** @see #addTick(double,Widget,int,int) addTick(double,Widget,int,int)
      **
      ** @param ticksPerLabel the ratio of the number of ticks,
      **  to the number of labeled ticks.
      **
      **/
     public void setTicksPerLabel(int ticksPerLabel) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        plotPanel.plotAreaNeedsUpdate();
        GChart.this.chartDecorationsChanged = true;
        if (ticksPerLabel <= 0)
           throw new IllegalArgumentException(
           "ticksPerLabel must be > 0");
        this.ticksPerLabel = ticksPerLabel;
     }

     /**
     * Sets this axes' tick length. Set the tick length to zero to
     * eliminate the tick entirely.
     * <p>
     * Note that GChart ticks are always "outside" ticks.
     *
     * @param tickLength the length of the tick.
     *
     * @see #getTickLength getTickLength
     *
     */
    abstract public void setTickLength(int tickLength);
     /**
     * Sets this axes' tick thickness.
     * <p>
     * <i>Tip:</i> Set the tick thickness to zero to
     * to hide the tick but retain the spacing between tick labels
     * and the axis due to the ticks' length.
     * <p>
     * Note that GChart ticks are always "outside" ticks.
     *
     * @param tickThickness the thickness of the tick.
     *
     * @see #getTickThickness getTickThickness
     * @see #setTickLength setTickLength
     *
     */
    abstract public void setTickThickness(int tickThickness);
    private void maybePopulateTicks() {
       if (tickCount > 0) populateTicks();
    }
    // fills in the tick positions when auto-generated.
    private void populateTicks() {
       AxisLimits l = getAxisLimits();

       tickInfo.clear();      
       for (int i = 0; i < tickCount-1; i++) {
          // linear interpolation between min and max
          double position =
             (l.min * ((tickCount-1)-i) + i * l.max)/(tickCount-1.0);
          tickInfo.add(new TickInfo(position, null, null, GChart.NAI, GChart.NAI))
       }
       // last tick is pulled out of the loop to allow
       // special case of 1 tick to be handled correctly:
       if (tickCount > 0) {
          tickInfo.add(new TickInfo(l.max, null, null, GChart.NAI, GChart.NAI))
       }

    }
      
    abstract void realizeAxis(PlotPanel pp);

    /* Draws the line associated with this axis, if it is visible*/
    protected void maybeRealizeAxis(PlotPanel pp) {
      if (axisVisible) realizeAxis(pp);
    }
     
   
    abstract void realizeTick(PlotPanel pp, int iTick);


    protected void getAxisLimits(AxisLimits result) {
       // so we get 1-unit changes between adjacent ticks
       final int DEFAULT_AXIS_RANGE = DEFAULT_TICK_COUNT-1;
       double min = getAxisMin();
       double max = getAxisMax();
       // Adjust min/max so that special cases, like one-point
       // charts, do not have axes that shrink down to a point,
       // which would create numerical and visual difficulties.
       if ((min!=min) && (max!=max)) { // x!=x is a faster isNaN
       // e.g. no data and no explicitly specified ticks
          min = 0;                                  
          max = min + DEFAULT_AXIS_RANGE;
       }
       else if ((min!=min) && !(max!=max)) { // x!=x is a faster isNaN
         // e.g. no data but only max explicitly set
          min = max - DEFAULT_AXIS_RANGE;
       }
       else if (!(min!=min) && (max!=max)) { // x!=x is a faster isNaN
         // e.g. no data but only min explicitly set
          max = min + DEFAULT_AXIS_RANGE;
       }
       else if (min == max) {
         // e.g one data point only, or they set min=max
          max = min + DEFAULT_AXIS_RANGE;
       }
       result.min = min;
       result.max = max;
    }
    AxisLimits getAxisLimits() {
       getAxisLimits(currentLimits);
       return currentLimits;
    }
      
    void rememberLimits() {
       getAxisLimits(previousLimits);
    }
    boolean limitsChanged() {
       boolean result = !getAxisLimits().equals(previousLimits);
       return result;
    }
   
    protected Symbol getGridSymbol() {
        return gridSymbol;
     }

    // returns the largest, explicitly specified, tick position
     protected double getTickMax() {
        double result = -Double.MAX_VALUE;
        for (int i = 0; i < tickInfo.size(); i++)
           result = Math.max(result, getTickPosition(i));
        return result;
     }

    // returns the smallest, explicitly specified, tick position
     protected double getTickMin() {
        double result = Double.MAX_VALUE;
        for (int i = 0; i < tickInfo.size(); i++)
           result = Math.min(result, getTickPosition(i));
        return result;
     }

    /* similar to getTickText, except for the tick position */
     protected double getTickPosition(int iTick) {
        double result = Double.NaN;      
        if (iTick < tickInfo.size()) {
           TickInfo ti = (TickInfo) tickInfo.get(iTick);
           result = ti.position;
        }
        return result;
     }

    protected Symbol getTickSymbol() {
        return tickSymbol;
     }
    /*
      * Returns any tick text associated with a tick index,
      * or null if there is no tick text associated with
      * this tick.
      * <p>
      *
      * This method assumes that the method populateTicks()
      * has already been executed, as needed, to generate
      * any auto-generated ticks.
      *
      * @param iTick the tick whose tick text is to be returned.
      * @return tick text, or null is no tick text.
      */
     protected String getTickText(int iTick) {
        String result = null;      
        if (iTick < tickInfo.size()) {
           TickInfo ti = (TickInfo) tickInfo.get(iTick);
           if (ti.tickLabel != null)
              result = ti.tickLabel;
           else if (null == ti.tickWidget &&
                    iTick % ticksPerLabel == 0)
              result = formatNumberAsTickLabel(ti.position);
        }
        return result;
    }

     protected Widget getTickWidget(int iTick) {
       TickInfo ti = (TickInfo) tickInfo.get(iTick);
       Widget result = ti.tickWidget;
       return result;
     }
     protected int getTickWidthUpperBound(int iTick) {
       TickInfo ti = (TickInfo) tickInfo.get(iTick);
       int result = ti.widthUpperBound;
       return result;
     }
     protected int getTickHeightUpperBound(int iTick) {
       TickInfo ti = (TickInfo) tickInfo.get(iTick);
       int result = ti.heightUpperBound;
       return result;
     }

    // Same as max, except treats NaN/MAX_VALUE values as "not there"
    protected double maxIgnoreNaNAndMaxValue(double x1, double x2) {
       double result;
       if ((x1!=x1) ||
           Double.MAX_VALUE == x1 ||
           -Double.MAX_VALUE == x1) // x!=x is a faster isNaN
          result = x2;
       else if ((x2!=x2) ||
                Double.MAX_VALUE == x2 ||
                -Double.MAX_VALUE == x2)
          result = x1;
       else
          result = Math.max(x1, x2);
       return result;
    }
    // Same as Math.min, except treats NaN/MAX_VALUE values as "not there"
    protected double minIgnoreNaNAndMaxValue(double x1, double x2) {
       double result;
       if ((x1!=x1) ||
           Double.MAX_VALUE == x1 ||
           -Double.MAX_VALUE == x1 ) // x!=x is a faster isNaN
          result = x2;
       else if ((x2!=x2) ||
                Double.MAX_VALUE == x2 ||
                -Double.MAX_VALUE == x2)
          result = x1;
       else
          result = Math.min(x1, x2);
       return result;
    }
    // sets the symbol used to render this axis' gridlines.
     protected void setGridSymbol(Symbol gridSymbol) {
        this.gridSymbol = gridSymbol;
     }
    // sets the symbol used to render this axis' ticks.
     protected void setTickSymbol(Symbol tickSymbol) {
        this.tickSymbol = tickSymbol;
     }
     // does a dummy set of any dynamically determined axis
     // limit, so, for update purposes, they are considered
     // to have changed.
     void invalidateDynamicAxisLimits() {
        // x!=x is a faster isNaN
        if ((axisMin!=axisMin)) setAxisMin(axisMin);
         if ((axisMax!=axisMax)) setAxisMax(axisMax);
     }
  } // end of class Axis      
  /**
   * Represents a curve on a chart, which includes
   * information such as the x,y coordinates of each point,
   * the symbol used to represent points on the curve, etc.
   * <p>
   * To create a new curve, use the <tt>GChart.addCurve</tt>
   * method.
   *
   * @see GChart#addCurve() addCurve()
   *
   */
  public class Curve {
     private boolean isVisible = true;
     private String legendHTML = null;
     private ArrayList points = new ArrayList();
     // symbol defines how every point on this curve is rendered
     private Symbol symbol = new Symbol(this);

     private YAxisId yAxisId = Y_AXIS;
    
     protected WidgetCursor widgetCursor = new WidgetCursor();

     WidgetCursor getWidgetCursor() { return widgetCursor;}
    
     /*
      * No public constructor because curves are always
      * contained within, and managed by, their containing
      * GChart via its addCurve, removeCurve, and related
      * methods.
      *
      */
     Curve() {
        super();
     }
     /**
     * Adds a new point to the curve, at the end of the current
     * list of points, with the specified
     * coordinates in model-units (arbitrary, application-specific,
     * units).
     * <p>
     *
     * GChart gives a special interpetation to the following values:
     * <p>
     *
     * <ol>
     *
     * <li> If <tt>-Double.MAX_VALUE</tt> is specified for either x or y,
     * the point acts as if it were placed at the minimum visible
     * x or y position within the plot area.
     * <p>
     *
     * <li> Similarly, if <tt>Double.MAX_VALUE</tt> is specified for
     * either x or y, the point acts as if it were placed at the
     * maximum visible x or y position within the plot area.  <p>
     *
     * <p>
     * <li>If <tt>Double.NaN</tt> is specified for either x or y, the
     * point is created, but it will not be visible in the
     * charting region.
     *
     * <p>
     * <i>Tip:</i> Connecting lines to/from such
     * <tt>Double.NaN</tt> points are elided, so you can use such a
     * point to create a break in an otherwise connected curve.
     *
     * </ol>
     *
     *
     * @param x the x-coordinate of the new point
     * @param y the y-coordinate of the new point
     *
     * @see #getPoint getPoint
     * @see #addPoint(int,double,double) addPoint(int,double,double)
     * @see #removePoint removePoint
     * @see #clearPoints clearPoints
     * @see #getNPoints getNPoints
     */
    public void addPoint(double x, double y) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       points.add(new Point(x, y));
    }

     /**
     * Adds a new point at the specified position in the point
     * sequence, increasing the indexes of existing points at or after
     * the specified position by 1.
     *
     * @param iPoint the position that the new point will occupy
     * @param x the x-coordinate of the new point (model units)
     * @param y the y-coordinate of the new point (model units)
     *
     * @see #getPoint getPoint
     * @see #addPoint(double, double) addPoint(double,double)
     * @see #removePoint removePoint
     * @see #clearPoints clearPoints
     * @see #getNPoints getNPoints
     */ 
    public void addPoint(int iPoint, double x, double y) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       points.add(iPoint, new Point(x, y));
    }

   /**
   * Removes every point this curve contains.
   *
   * @see Point Point
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #getNPoints getNPoints
   */
   public void clearPoints() {
      plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
      points.clear();
   }

  /**
   * @deprecated
   *
   * This method is equivalent to:
   * <p>
   *<tt>getSymbol().getHovertextTemplate()</tt>
   * <p>
   *
   * It is retained only for GChart 1.1 compatibility purposes.
   *
   * @see Symbol#getHovertextTemplate() Symbol.getHovertextTemplate
   *
   */
    public String getHovertextTemplate() {
      return symbol.getHovertextTemplate();
    }

  /**
   ** Returns the HTML defining this curve's legend label.
   **
   ** @return the legend label HTML for this curve
   **
   ** @see #setLegendLabel setLegendLabel
   **
   **/
    public String getLegendLabel() {
      return legendHTML;
    }
 
  /**
   * Returns the number of points this curve contains.
   *
   * @return the number of points this curve contains.
   *
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #clearPoints clearPoints
   */ 
    public int getNPoints() {
      return points.size();
    }
 
  /**
   * Convenience method equivalent to <tt>getPoint(getNPoints()-1)</tt>.
   * <p>
   * This method makes code more readable for the common case when
   * you first add a point to the end of a curve, and then modify that
   * point's attributes, as illustrated below:
   * <p>
   * <pre>
   *   class MyChart extends GChart {
   *     public MyChart() {
   *       addCurve();
   *       for (int i=0; i < 10; i++) {
   *         getCurve().addPoint(i,i);
   *         getCurve().getPoint().setAnnotationText("Point " + i);
   *       }
   *       update();
   *     }
   *   }
   * </pre>
   *
   * @return the point on the curve with the highest integer index
   *
   * @see #getPoint(int) getPoint(int)
   * @see #getNPoints() getNPoints()
   *
   */
    public Point getPoint() {
       Point result = getPoint(getNPoints()-1);
       return result;
    }

  /**
   * Returns a reference to the point at the specified
   * index.  The returned reference can be used to modify
   * various properties of the point, such as
   * its optional annotation (text label).
   *
   * <p>
   * @param iPoint the index of the point to be returned.
   * @return a reference to the Point at the specified index.
   *
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #removePoint removePoint
   * @see #clearPoints clearPoints
   * @see #getNPoints getNPoints
   */
    public Point getPoint(int iPoint) {
       if (iPoint < 0 || iPoint >= points.size())
          throw new IllegalArgumentException(
           "Point index iPoint=" + iPoint + ". " +
           "is either < 0 or >= the number of points on the curve.");
       Point result = (Point) points.get(iPoint);
       return result;
    }
  /**
    ** Returns the symbol associated with this curve.
    ** <p>
    **
    ** Though you cannot set the symbol itself (there is no
    ** <tt>setSymbol</tt> method) you can have essentially
    ** the same effect by setting the <tt>SymbolType</tt> (to get
    ** qualitatively different kinds of symbols, e.g.
    ** bar-chart bars vs boxes) and by changing symbol
    ** attributes such as background color, height, and
    ** width.
    **
    ** @return the symbol used to represent points on this curve
    **
    ** @see Symbol#setSymbolType Symbol.setSymbolType
    ** @see Symbol#setBackgroundColor Symbol.setBackgroundColor
    ** @see Symbol#setBorderWidth Symbol.setBorderWidth
    ** @see Symbol#setBorderStyle Symbol.setBorderStyle
    ** @see Symbol#setWidth Symbol.setWidth
    ** @see Symbol#setHeight Symbol.setHeight
    ** @see Symbol#setModelWidth Symbol.setModelWidth
    ** @see Symbol#setModelHeight Symbol.setModelHeight
    **
    **/
     public Symbol getSymbol() {
       return symbol;
     }
 
  /**
   * Returns the y-axis (Y_AXIS or Y2_AXIS) this curve is
   * plotted on.
   *
   * @return an identifier, either Y_AXIS, or Y2_AXIS, indicating
   *   if this curve is plotted on the left (y) or right (y2) y-axis
   *
   ** @see #setYAxis setYAxis
   ** @see GChart#Y_AXIS Y_AXIS
   ** @see GChart#Y2_AXIS Y2_AXIS
   **
   */
    public YAxisId getYAxis() {
      return yAxisId;
    }

  /** Is this curve visible on the chart and legend key,
    ** or is it hidden from view.
    **
    ** @return true if the curve is visible, false otherwise.
    **
    ** @see #setVisible setVisible
    **/
     public boolean isVisible() {return isVisible;}

  /** Convenience method equivalent to <tt>getYAxis()==Y2_AXIS</tt>.
   *
   * @return true if curve is on second y-axis, else false
   *
   * @see #getYAxis getYAxis
   */
    public boolean onY2() {
       return yAxisId == Y2_AXIS;
    }
  /**
   * Removes the point at the specified index.
   *
   * @param iPoint index of point to be removed.
   *
   * @see #getPoint getPoint
   * @see #addPoint(double, double) addPoint(double,double)
   * @see #addPoint(int,double,double) addPoint(int,double,double)
   * @see #clearPoints clearPoints
   * @see #getNPoints getNPoints
   */
    public void removePoint(int iPoint) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       points.remove(iPoint);
    }
  /**
   ** @deprecated
   **
   ** This method is equivalent to:
   ** <p>
   **  <tt>getSymbol().setHovertextTemplate(hovertextTemplate)</tt>
   ** <p>
   ** It is retained only for GChart 1.1 compatibility purposes.
   **
   ** @see Symbol#setHovertextTemplate Symbol.setHovertextTemplate
   **/
    public void setHovertextTemplate(String hovertextTemplate) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       symbol.setHovertextTemplate(hovertextTemplate);
    }
  /**
   ** Sets the HTML that defines the label shown to the
   ** right of the icon representing the curve's symbol in
   ** the chart's legend.
   **
   ** <p>
   ** Setting the legend label to <tt>null</tt> removes the
   ** entire row (the label and the icon) associated with
   ** this curve from the chart key.
   ** <p>
   ** Note that, since <tt>null</tt> is the default, unless
   ** you set at least one legend label, no chart key will
   ** appear at all.
   **
   ** @param legendHTML the HTML defining this curve's legend label.
   **                   or <tt>null</tt> to remove the curve from
   **                   the legend entirely.
   **                  
   ** @see #getLegendLabel getLegendLabel
   ** @see GChart#setLegendThickness setLegendThickness
   **
   **/
    public void setLegendLabel(String legendHTML) {
       GChart.this.chartDecorationsChanged = true;
       this.legendHTML = legendHTML;
    }
  /**
    ** Defines if this curve is visible both in the plotting
    ** region and on the legend key.
    ** <p>
    **
    ** <i>Notes:</i>
    **
    ** <ol>
    **  <li>A curve must also have a non-<tt>null</tt> legend label
    ** if it is to appear on the legend key.
    ** <p>
    **
    **  <li>Hidden curves are excluded from the computation
    ** of any auto-computed axis limits.
    **
    ** </ol>
    **
    ** @param isVisible false to hide curve, true to reveal it.
    **
    ** @see #isVisible() isVisible
    ** @see #setLegendLabel setLegendLabel
    **
    **/
     public void setVisible(boolean isVisible) {
        plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
        chartDecorationsChanged = true; // may change chart's legend
        this.isVisible = isVisible;
     }
    
  /** Sets the y-axis that this curve is plotted on.
   ** <p>
   ** @param axisId must be either GChart.Y_AXIS or
   **               GChart.Y2_AXIS
   **
   ** @see #getYAxis getYAxis
   ** @see GChart#Y_AXIS Y_AXIS
   ** @see GChart#Y2_AXIS Y2_AXIS
   **
   **
   **/
    public void setYAxis(YAxisId axisId) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       yAxisId = axisId;
    }

  // renders the specified point of this curve on the given panel
  void realizePoint(PlotPanel pp, int iPoint) {
     Point p = (Point) points.get(iPoint);
     double x = p.getX();
     double y = p.getY();
     // skip points at undefined locations
     if ((x!=x) || (y!=y)) return; // x!=x is a faster isNaN
     double prevX = Double.NaN;
     double prevY = Double.NaN;
     if (iPoint > 0) {
        Point prevP = (Point) points.get(iPoint-1);
        prevX = prevP.getX();
        prevY = prevP.getY();
     }
     double nextX = Double.NaN;
     double nextY = Double.NaN;
     if (iPoint < getNPoints()-1) {
        Point nextP = (Point) points.get(iPoint+1);
        nextX = nextP.getX();
        nextY = nextP.getY();
     }
       getSymbol().realizeSymbol(pp,  p.getAnnotation(),
                                 onY2(), getShowOffChartPoints(),
                                 x, y,
                                 prevX, prevY, nextX, nextY);
//    }
  }
  /**
   ** Represents a single point on one of the chart's
   ** curves. This includes the x, y values of the point in
   ** "model coordinates" (arbitrary, application-specific,
   ** units), as well as an optional annotation (text label)
   ** for the point.
   ** <p>
   ** To create points, use a curve's <tt>addPoint</tt> method.
   **
   ** @see Curve#addPoint addPoint
   **/
  public class Point {
     // x, y location (user coordinates) of point
     // (points are drawn using the containing curve's symbol)
     private double x;
     private double y;
     Annotation annotation = null;
     Point(double x, double y) {
       this.x = x;  
       this.y = y;
     }
     /**
      ** Returns true if annotation will be rendered in a bold,
      ** or false if in normal, weight font.
      **
      ** @return if this annotation is in bold or not.
      **
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      **/
     public String getAnnotationFontWeight() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontWeight();
     }

     /**
      ** Returns the color of the font used to display the point's
      **   annotation text.
      **  
      ** @return CSS color string defining the annotation's color
      **
      ** @see #setAnnotationFontColor setAnnotationFontColor
      **/
     public String getAnnotationFontColor() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontColor();
     }

    
     /**
      ** Returns the CSS font-style in which the text of this
      **  annotation will be rendered.
      **
      ** @return the font-style used by this annotation (italic,
      **   normal, etc.)
      **
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      **/
     public String getAnnotationFontStyle() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontStyle();
     }

     /**
      ** Returns the font size of this point's annotation
      ** (text label), in pixels.
      **
      ** @return the font size of this point's annotation.
      **
      ** @see #setAnnotationFontSize setAnnotationFontSize
      **/
     public int getAnnotationFontSize() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getFontSize();
     }

    
    
     /** Returns the previously specified location, relative
      ** to the symbol representing the point, of the
      ** annotation (text label) associated with this point.
      **
      ** @return relative location of the point's annotation
      **
      ** @see #setAnnotationLocation setAnnotationLocation
      **
      **/
     public AnnotationLocation getAnnotationLocation() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getLocation();
     }
     /**
      ** Returns the text of this point's annotation.
      **
      ** @return the text of the annotation, or null if this
      **   point lacks an annotation.
      **
      ** @see #setAnnotationText setAnnotationText
      **/
     public String getAnnotationText() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getText();
     }

     /**
      * Returns the widget reference that defines this point's
      * annotation as previously specified by
      * <tt>setAnnotationWidget</tt>. Returns <tt>null</tt> if
      * the annotation has not yet been specified, or if it was
      * defined via <tt>setAnnotationText</tt>.
      *
      * @return reference to the widget defining this point's
      * annotation, or <tt>null</tt> if none.
      *
      * @see #setAnnotationWidget setAnnotationWidget
      * @see #setAnnotationText setAnnotationText
      *
      */

     public Widget getAnnotationWidget() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getWidget();
     }
    
     /**
      ** Returns true is the point's annotation is visible, false
      ** otherwise
      **
      ** @return if the a annotation defined for this point will
      **    be visible or not after the next update.
      **
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public boolean getAnnotationVisible() {
        if (null == annotation) annotation = new Annotation();  
       return annotation.getVisible();
     }

     /**
      ** Returns the distance, in pixels, that this annotation
      ** will be shifted along the x-axis from it's default
      ** location.  <p>
      **
      ** @return amount annotation will be shifted along the x-axis,
      **   in pixels.
      **  
      ** @see #setAnnotationXShift setAnnotationXShift
      **/
      public int getAnnotationXShift() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getXShift();
      }
    
     /**
      ** Returns the distance, in pixels, that this annotation
      ** will be shifted along the y-axis from it's default
      ** location.  <p>
      **
      ** @return amount annotation will be shifted along the y-axis,
      **   in pixels.
      **  
      ** @see #setAnnotationYShift setAnnotationYShift
      **/
      public int getAnnotationYShift() {
        if (null == annotation) annotation = new Annotation();  
        return annotation.getYShift();
      }

      /** Returns the <tt>Curve</tt> that this point was added to.
       **
       ** @return a reference to the <tt>Curve</tt> that contains
       ** this point (its "parent").
       **
       **/
      public Curve getParent() {return Curve.this;}
       /* returns the WidgetCursor of the curve that has this Point*/
      WidgetCursor getParentWidgetCursor() {
          return Curve.this.getWidgetCursor();
      }
     
     /** Returns the x-coordinate of this point in "model units"
      ** (arbitrary, application-specific, units).
      **
      ** @return the x-coordinate, in model units
      **
      ** @see #setX setX
      ** @see #setY setY
      ** @see #getY getY
      **
      **/
     public double getX() {
        return x;
     }
     /** Returns the y-coordinate of this point in "model units"
      ** (arbitrary, application-specific, units).
      **
      ** @return the y-coordinate, in model units
      **
      ** @see #getX getX
      ** @see #setX setX
      ** @see #setY setY
      **
      **/
     public double getY() {
        return y;
     }

     /**
      ** Specifies the weight of the font that will be used
      ** to render the text of this point's annotation.
      ** <p>
      **
      ** @param cssWeight A standard CSS font-weight
      **    specification such as normal, bold, bolder, lighter,
      **    100, 200, ... 900, or inherit
      **
      ** @see #getAnnotationFontWeight getAnnotationFontWeight 
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontWeight(String cssWeight) {
       plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
       if (null == annotation) annotation = new Annotation();  
       annotation.setFontWeight(cssWeight);
     }
     /**
      ** Specifies the color of the annotation's font.
      **
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display this
      **    point's annotation message.
      **
      ** @see #getAnnotationFontColor getAnnotationFontColor 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontColor(String cssColor) {
        plotPanel.invalidateWidgetsAfterCursor(
            getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontColor(cssColor);
     }

    
     /**
      ** Specifies the CSS font-style used by this point's annotation
      ** message.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getAnnotationFontStyle getAnnotationFontStyle 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontStyle(String cssStyle) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontStyle(cssStyle);
     }
     /**
      ** Specifies the font size of this point's annotation, in
      ** pixels.
      **
      ** @param fontSize the font size of this point's annotation, in
      **   pixels.
      **
      ** @see #getAnnotationFontSize getAnnotationFontSize 
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      **/
     public void setAnnotationFontSize(int fontSize) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setFontSize(fontSize);
     }
     /**
      ** Specifies the location, relative to this point's symbol,
      ** of this point's annotation (text label).
      ** <p>
      **
      ** You can further adjust the position of a point's
      ** annotation by specifying non-zero positional shifts via
      ** the <tt>setAnnotationXShift</tt> and
      ** <tt>setAnnotationYShift</tt> methods.
      **
      **
      ** @param annotationLocation the relative location of
      ** the annotation
      **
      ** @see #getAnnotationLocation getAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationVisible setAnnotationVisible
      **
      **/
     public void setAnnotationLocation(AnnotationLocation
                                       annotationLocation) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setLocation(annotationLocation);
     }

     /**
      ** Specifies the text of this point's annotation
      ** (label).
      ** <p>
      **
      ** <p>By default text is plain text, though
      ** you can change the size, weight, style, and color of
      ** the text via the <tt>setAnnotationFont*</tt>
      ** family of methods.
      **
      ** <p>
      **
      ** <b>To use HTML, <i>your text must begin with</i>
      ** <tt>&lt;html&gt</tt></b> (otherwise, GChart will treat
      ** it as plain text). Note that the leading
      ** <tt>&lt;html&gt</tt> is stripped off by GChart before
      ** your HTML gets to the browser. Since it's just a flag
      ** for GChart, not a real HTML tag, you should <i>not</i>
      ** use a closing <tt>&lt;/html&gt</tt> at the end.
      **
      ** <p> <small> The idea for adding HTML support (only plain
      ** text was supported originally) came from <a
      ** href="http://groups.google.com/group/Google-Web-Toolkit/msg/cb89003dad2416fe">
      ** this GWT forum post by Malcolm Gorman</a>. The current
      ** HTML support (and, it's natural extension, Widget
      ** support) in tick labels and annotations, which seems so
      ** obvious in hindsight, might never have been added had it
      ** not been for this post. Thanks!</small>
      **
      ** <p>
      **
      ** <small><b>How to use the width and height upperbounds:</b>
      ** </small>
      **
      ** <p>
      **
      ** <blockquote><small>
      **
      **
      ** In most cases, you can safely ignore these two
      ** parameters, simply calling the {@link
      ** #setAnnotationText(String) 1-arg convenience method}
      ** and getting GChart to estimate them for you.
      ** <p>
      **
      ** The width and height upperbounds define an invisible
      ** bounding box (a 1x1 GWT Grid, actualy) that is used to
      ** properly align and center your annotation.
      ** <p>
      **
      ** <p> Annotations can
      ** become misaligned if, say, due to the user zooming up
      ** their font size, an annotation's size exceeds these
      ** upperbounds. This misalignment problem can be fixed by
      ** specifying a larger width and/or height upperbound.
      ** But, larger upperbounds slow chart updates a bit.
      ** The defaults try to balance the performance and
      ** alignment tradeoff.
      **
      ** </blockquote></small>
      **
      ** @param annotationText the text or (<tt>&lt;html&gt</tt>
      ** prefixed) HTML of this point's
      ** annotation, or null to remove all annotation.
      **
      ** @param widthUpperBound an upper bound on the width of
      ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      ** get GChart to estimate this width using a heuristic
      ** that works fine most of the time.
      **
      ** @param heightUpperBound an upper bound on the height of
      ** the text or HTML, in pixels. Use <tt>GChart.NAI</tt> to
      ** get GChart to estimate this height using a heuristic
      ** that works fine most of the time.
      **
      ** @see #getAnnotationText getAnnotationText
      ** @see #setAnnotationText(String) setAnnotationText(String)
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see GChart.Axis#addTick(double,String,int,int) addTick
      **
      **/
     public void setAnnotationText(String annotationText,
                                   int widthUpperBound,
                                   int heightUpperBound) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setText(annotationText,
                           widthUpperBound,
                           heightUpperBound);
     }
     /**
      * Sets the text of an annotation.
      * <p>
      * This is a convenience method equivalent to
      * <tt>setAnnotationText(annotationText, GChart.NAI, GChart.NAI)</tt>. See
      * that method for further details.
      * <p>
      *
      ** @param annotationText the text or
      ** (<tt>&lt;html&gt</tt>-prefixed) HTML of this point's
      ** annotation, or null to remove all annotation.
      *
      * @see #setAnnotationText(String, int, int)
      *       setAnnotationText(String,int,int)
      *      
      */
     public void setAnnotationText(String annotationText) {
          setAnnotationText(annotationText, GChart.NAI, GChart.NAI);
     }

     /**
      ** Specifies a widget defining this point's annotation
      ** <p>
      ** This method is similar to <tt>setAnnotationText</tt>
      ** except that it uses a widget, rather than a string
      ** to define this point's annotation.
      ** Although the string based method is faster
      ** on first chart rendering, and uses less memory, the
      ** widget-based method allows you to change the annotation
      ** independently of the chart--potentially bypassing (or
      ** at least speeding up) expensive chart updates later on.
      ** <p>
      **
      ** You might use a widget-based annotation to pop-up a
      ** message whenever the user clicks on a button underneath
      ** a particular data point on the chart, to include a small
      ** GWT <tt>Grid</tt> as a table embedded in the upper left
      ** hand corner of the chart, to trigger mouse-over events
      ** when the user hovers over a transparent image-based
      ** annotation centered on a particular point, etc.
      ** <p>
      **
      ** <i>Tip:</i>If you need to instrument a chart using
      ** widgets precisely positioned on the chart, but not
      ** associated with any visible curve, add a curve just to
      ** hold these annotations, with one point per annotation,
      ** and set that curve's symbol type
      ** to <tt>SymbolType.NONE</tt>.
      **
      **
      *  @param annotationWidget the GWT Widget that defines this
      *    point's annotation.
      *
      *  @param widthUpperBound an upper bound on the width of
      *  the Widget, in pixels. If this and the next
      *  parameter are omitted, GChart will use
      *  <tt>DEFAULT_WIDGET_WIDTH_UPPERBOUND</tt>.
      * 
      *  @param heightUpperBound an upper bound on the height of
      *  the Widget, in pixels. If this and the previous
      *  parameter are omitted, GChart will use <tt>
      *  DEFAULT_WIDGET_HEIGHT_UPPERBOUND</tt>
      *
      *
      * @see #getAnnotationWidget getAnnotationWidget
      * @see #setAnnotationText(String, int, int)
      *       setAnnotationText(String,int,int)
      * @see #setAnnotationWidget(Widget)
      * setAnnotationWidget(Widget)
      * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      * @see SymbolType#NONE  SymbolType.NONE
      *
      **/
     public void setAnnotationWidget(Widget annotationWidget,
                                   int widthUpperBound,
                                   int heightUpperBound) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
     // accept "Not an Integer" (because setAnnotationText does)
       if (widthUpperBound == GChart.NAI)
          widthUpperBound = DEFAULT_WIDGET_WIDTH_UPPERBOUND;
       if (heightUpperBound == GChart.NAI)
          heightUpperBound = DEFAULT_WIDGET_HEIGHT_UPPERBOUND;
       annotation.setWidget(annotationWidget,
                            widthUpperBound,
                            heightUpperBound);
     }

     /**
      * Specifies a widget defining this point's annotation.
      * <p>
      * A convenience method equivalent to
      * <tt>setAnnotationWidget(annotationWidget,
      * DEFAULT_WIDGET_WIDTH_UPPERBOUND,
      * DEFAULT_WIDGET_HEIGHT_UPPERBOUND)</tt>
      *
      *  @param annotationWidget the GWT Widget that defines this
      *    point's annotation.
      *
      * @see #setAnnotationWidget(Widget,int,int)
      * setAnnotationWidget(Widget,int,int)
      * @see #DEFAULT_WIDGET_HEIGHT_UPPERBOUND DEFAULT_WIDGET_HEIGHT_UPPERBOUND
      * @see #DEFAULT_WIDGET_WIDTH_UPPERBOUND DEFAULT_WIDGET_WIDTH_UPPERBOUND
      *
      */
     public void setAnnotationWidget(Widget annotationWidget) {
        setAnnotationWidget(annotationWidget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                          DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
     }

    
     /**
      ** Specifies if this point's annotation
      ** (label) is visible or not.
      ** <p>
      **
      ** @param isVisible use true to make the annotation
      **   visible, or false to hide it.
      **
      ** @see #getAnnotationVisible getAnnotationVisible
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationText setAnnotationText
      **/
      public void setAnnotationVisible(boolean isVisible) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setVisible(isVisible);
      }

     

     /**
      ** Specifies the number of pixels (along the x-axis) to
      ** move this point's annotation from its default,
      ** <tt>AnnotationLocation</tt>-defined, position.  Negative
      ** values move the annotation in the negative x direction.
      **
      ** <p> For example, with the default <tt>xShift</tt> of 0,
      ** annotations with an <tt>AnnotationLocation</tt> of
      ** <tt>EAST</tt> will have their left edges flush against
      ** the right edge of, say, a box symbol representing the
      ** annotated point.  You could use an <tt>xShift</tt>
      ** setting of 10 to move the annotation 10 pixels to the
      ** right and thus introduce some space between the
      ** annotation and the box.
      ** <p>
      **
      ** <i>Special convention for pie slices:</i>
      ** Points on curves whose symbols represent pie
      ** slices always have the positive x-axis associated with
      ** the shifts specified by this method aligned with the
      ** outward-pointing pie radius that bisects the pie slice. This
      ** convention makes it easy to move pie slice annotations
      ** radially outward (via <tt>xShift > 0</tt>) or
      ** radially inward (via <tt>xShift < 0</tt>). For those
      ** rare situations where you may need to move a pie
      ** annotation perpendicularly to this radius, use
      ** <tt>setAnnotationYShift</tt>.
      **
      ** @param xShift number of pixels to move annotation
      **   along the x axis from
      **   it's default, <tt>AnnotationLocation</tt>-defined,
      **   location.
      **
      ** @see #setAnnotationYShift setAnnotationYShift
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see #getAnnotationXShift getAnnotationXShift
      **/
      public void setAnnotationXShift(int xShift) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setXShift(xShift);
      }
     /**
       ** Specifies the number of pixels (along the y-axis) to
      ** move this point's annotation from its default,
      ** <tt>AnnotationLocation</tt>-defined, position.  Negative
      ** values move the annotation in the negative y direction.
      **
      ** <p> For example, with the default <tt>yShift</tt> of 0,
      ** annotations with an <tt>AnnotationLocation</tt> of
      ** <tt>SOUTH</tt> will have their top edges flush against
      ** the bottom edge of, say, a box symbol representing the
      ** annotated point.  You could use a <tt>yShift</tt>
      ** setting of -10 to move the annotation down 10 pixels and
      ** thus introduce some spacing between the annotation and
      ** the box.
      ** <p>
      **
      ** <i>Special convention for pie slices:</i> The positive
      ** y-axis for pie slices always points one 90 degree
      ** counter-clockwise rotation from the direction of the
      ** outward-pointing pie radius that bisects the pie slice.
      ** This convention means that <tt>yShift</tt> moves pie
      ** slice annotations along a line <i>perpendicular to</i>
      ** this bisecting pie radius. Use the companion method
      ** <tt>setAnnotationXShift</tt> for the more common
      ** operation of moving the annotation along this bisecting
      ** radius.
      **
      ** @param yShift number of pixels to move annotation along
      **   the y-axis from it's default,
      **   <tt>AnnotationLocation</tt>-defined, location.
      **
      ** @see #setAnnotationXShift setAnnotationXShift
      ** @see #setAnnotationLocation setAnnotationLocation
      ** @see #setAnnotationFontWeight setAnnotationFontWeight
      ** @see #setAnnotationFontColor setAnnotationFontColor
      ** @see #setAnnotationFontStyle setAnnotationFontStyle
      ** @see #setAnnotationFontSize setAnnotationFontSize
      ** @see #setAnnotationText setAnnotationText
      ** @see #setAnnotationVisible setAnnotationVisible
      ** @see #getAnnotationXShift getAnnotationXShift
      **/
      public void setAnnotationYShift(int yShift) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        if (null == annotation) annotation = new Annotation();  
        annotation.setYShift(yShift);
      }

     /**
      * Defines the x-coordinate of this point in "model units"
      * (arbitrary, application-specific, units mapped to the
      * horizontal dimension of the plot area).
      * <p>
      *
      * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
      * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
      * <tt>addPoint</tt> method for details.
      *
      *
      * @param x the x-coordinate of the point in model units.
      *
      ** @see #getX getX
      ** @see #setY setY
      ** @see #getY getY
      ** @see #addPoint addPoint
      */
     public void setX(double x) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.x = x;
     }

     /**
      * Defines the y-coordinate of this point in "model units"
      * (arbitrary, application-specific, units mapped to the
      * vertical dimension of the plot area).
      * <p>
      *
      * <tt>Double.NaN</tt>, <tt>Double.MAX_VALUE</tt>, and
      * <tt>-Double.MAX_VALUE</tt> have special meanings. See the
      * <tt>addPoint</tt> method for details.
      *
      * @param y the y-coordinate of the point in model units.
      *
      ** @see #getX getX
      ** @see #setX setX
      ** @see #getY getY
      ** @see #addPoint addPoint
      **
      */
     public void setY(double y) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.y = y;
     }

     Annotation getAnnotation() {
        if (annotation == null) annotation = new Annotation();
        return annotation;
     }
    
  } // end of class GChart.Curve.Point
} // end of class GChart.Curve      
 

     // Allows hovertext templates to be parsed into "chunks"
     // so that they can be expanded into hovertext faster.
     static class HovertextChunk {
        final static int HOVERTEXT_PARAM_NONE = 0; // plain old text
        final static int HOVERTEXT_PARAM_X = 1// ${x}
        final static int HOVERTEXT_PARAM_Y = 2// ${y}
        final static int HOVERTEXT_PARAM_PIESLICESIZE = 3; // ${pieSlicePercent}
        int paramId;      // id of substitution parmeter
        String chunkText; // plain text that follows this parameter
        HovertextChunk(int id, String text) {
           paramId = id;
           chunkText = text;
        }
        // returns array of "chunks" corresponding to the given
        // hovertext template
        static HovertextChunk[] parseHovertextTemplate(
           String htTemplate) {
           if (htTemplate.equals("")) return new HovertextChunk[0];
           // takes "x=${x}; y=${y}" into {"x=", "x}; y=", "y}"}
           // Thus, except for the first, chunks contain a
           // keyword like part, followed by a string literal.
           String[] sChunk = htTemplate.split("\\$\\{");
           HovertextChunk[] result = new HovertextChunk[sChunk.length];

           for (int i = 0; i < sChunk.length; i++) {
              String sC = sChunk[i];
              if (sC.startsWith("x}"))
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_X,
                                   sC.substring("x}".length()));
              else if (sC.startsWith("y}"))
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_Y,
                                   sC.substring("y}".length()));
              else if (sC.startsWith("pieSliceSize}"))
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_PIESLICESIZE,
                                   sC.substring("pieSliceSize}".length()));
              else
                 // a ${ that is not followed by a recognized
                 // keyword is interpreted literally--so, except
                 // for the first chunk, we need to add it back.
                 result[i] = new HovertextChunk(
                                   HOVERTEXT_PARAM_NONE,
                                   (i == 0) ? sC : ("${" + sC));
           }
           return result;
        }
     }

     // static so it can be called by ReusableImage class
     // to generate hovertext when user hovers over it.
     static String getHovertext(HovertextChunk[] htc,
                         double x, double y, boolean onY2,
                         double pieSliceSize,
                         Axis xAxis,
                         Axis yAxis,
                         Axis y2Axis) {
       String result = "";
      
       String xS = null;
       String yS = null;
       String pieSlicePercentS = null;
       for (int i = 0; i < htc.length; i++) {
          switch (htc[i].paramId) {
             case HovertextChunk.HOVERTEXT_PARAM_NONE:
                break;
             case HovertextChunk.HOVERTEXT_PARAM_X:
                if (null == xS)
                   xS = xAxis.formatNumberAsTickLabel(x);
                result += xS;
                break;
             case HovertextChunk.HOVERTEXT_PARAM_Y:
                if (null == yS) {
                  yS = onY2 ? y2Axis.formatNumberAsTickLabel(y) :
                       yAxis.formatNumberAsTickLabel(y);
                }
                result+=yS;
                break;
                 
             case HovertextChunk.HOVERTEXT_PARAM_PIESLICESIZE:
                if (null == pieSlicePercentS) {
                  pieSlicePercentS = onY2 ?
                   y2Axis.formatNumberAsTickLabel(100*pieSliceSize) :
                   yAxis.formatNumberAsTickLabel(100*pieSliceSize);
                }
                result+=pieSlicePercentS + "%";
                break;
               
             default:
                throw new IllegalStateException("An illegal HOVERTEXT_PARAM_* id: " + htc[i].paramId +
                                                " was encountered. A GChart bug is likely to blame.");
          }
          result+=htc[i].chunkText;
       }
       return result;
     }
    

    
  /**
    ** Defines a chart curve symbol. Each point on a curve
    ** is represented on the chart by an appropriate
    ** rendering of the curve's symbol.
    **
    ** @see Curve#getSymbol Curve.getSymbol
    ** @see SymbolType SymbolType
    **
    */
  public class Symbol {
     private String hovertextTemplate=null;
     private HovertextChunk[] hovertextChunks = null;
     private Annotation annotation = null;
     private String backgroundColor = DEFAULT_SYMBOL_BACKGROUND_COLOR;
     private double baseline = Double.NaN;
     private String borderColor = null;
     private String  borderStyle = DEFAULT_SYMBOL_BORDER_STYLE;
     private int  borderWidth = DEFAULT_SYMBOL_BORDER_WIDTH;
     private boolean fillHasHovertext = true;
     private double fillSpacing = Double.NaN;
     private int fillThickness = GChart.NAI;
     private int height = DEFAULT_SYMBOL_HEIGHT;
// XXX: Symbols are used independently of Curves by the
// realizeTick method. But it's probably better to render ticks
// via specialized system curves. If/when that's implemented,
// Symbol should become an inner class of Curve, and this
// explicit parent pointer will no longer be required. 
     private Curve parent = null;
// when specified, model width/height are in user-defined units.
     private double modelHeight = Double.NaN;
     private double modelWidth = Double.NaN;
// NaN means "begin this slice where last slice left off, or at
// initialPieSliceOrientation if it is the first slice to be rendered"
     private double pieSliceOrientation = Double.NaN;
// slices, by default, fill the entire pie (useful for drawing disks)
     private double pieSliceSize = 1;

     private SymbolType symbolType = DEFAULT_SYMBOL_TYPE;
    
     private int width = DEFAULT_SYMBOL_WIDTH;
     private String imageURL = null;
    
     Symbol(Curve parent) {super(); this.parent = parent;}


    
    // Returns the hover text appropriate for a point on this curve
     String getHovertext(double x, double y, boolean onY2) {
        return GChart.getHovertext(getHovertextChunks(), x, y, onY2,
                            getPieSliceSize(), xAxis, yAxis, y2Axis);
     }

    // hovertext associated with a filled-in or interpolated element
    HovertextChunk[] getFilledElementHovertextChunks() {
       HovertextChunk[] result = null;
       if (getFillHasHovertext()) result = getHovertextChunks();
       return result;
     }
    
     /** Returns the CSS background color of all the rectangular
      ** elements used in rendering the symbol.
      **
      ** @return the CSS background color used to fill in the
      **  central (non-border) part of each rectangular element
      **  used to render a curve's symbol.
      **
      ** @see #setBackgroundColor(String) setBackgroundColor
      **/
     public String getBackgroundColor() {
        String result;
        if (null == backgroundColor)
           result = DEFAULT_SYMBOL_BORDER_COLORS[
                      getCurveIndex(getParent()) %
                      DEFAULT_SYMBOL_BORDER_COLORS.length];
        else
           result = backgroundColor;
        return result;
     }
     /** Returns the baseline value for this symbol,
      ** previously specified via <tt>setBaseline</tt>
      **
      **
      ** @return the previously specified baseline value for
      **   this symbol.
      **
      ** @see #setBaseline setBaseline
      **/
     public double getBaseline() {
        return baseline;
     }
    /** Returns the CSS border color of all the rectangular
     ** elements used in rendering the symbol.
     **
     ** <p>
     ** @return the color of the border of the rectangular elements
     **   used to render the symbol, in standard CSS format
     **
     ** @see #setBorderColor setBorderColor
     **
     **/
     public String getBorderColor() {
        String result;
        if (borderColorNotSpecified())
           result = "black";
//           result = DEFAULT_SYMBOL_BORDER_COLORS[
//                      getCurveIndex(getParent()) %
//                      DEFAULT_SYMBOL_BORDER_COLORS.length];
        else
           result = borderColor;
        return result;
     }

     /**
     ** Returns the border style of all of the rectangular
     ** elements from which this symbol is built.
     ** <p>
     ** @return the CSS borderStyle of this symbol's elements
     **         (dotted, dashed, solid, etc. )
     ** 
     ** @see #setBorderStyle setBorderStyle
     **/
       public String getBorderStyle() {
          return borderStyle;
       }

     /**
     ** Returns the width of the border around each
     ** rectangular element used to render this symbol,
     ** in pixels.
     **
     ** <p>
     ** @return the previously set border width (in pixels).
     **
     ** @see #setBorderWidth setBorderWidth
      */
       public int getBorderWidth() {
          return borderWidth;
       }

     /**
      ** Returns a value indicating if interpolated and filled-in
      ** elements associated with non-rectangular aspects of a
      ** symbol get the same kind of hovertext as non-filled
      ** elements do.
      **
      ** @return true if hovertext is added to filled-in elements
      **         false if it is not.
      **        
      ** @see #setFillHasHovertext setFillHasHovertext
      **
      **/
       public boolean getFillHasHovertext() {
         return fillHasHovertext;
       }
      
      
     /**
     ** Returns the spacing between successive rectangular
     ** elements used to emulate any required non-rectangular
     ** features of the symbol.  <p>
     **
     **
     **
     ** @return the previously set (or the default, if the
     ** fillSpacing has been set to <tt>Double.NaN</tt>) fill spacing
     ** (in pixels).
     **
     ** @see #setFillSpacing setFillSpacing
     ** @see #setFillThickness setFillThickness
     **
      */
       public double getFillSpacing() {
         if ((fillSpacing!=fillSpacing)) // x!=x is a faster isNaN
           return symbolType.defaultFillSpacing();
         else
           return fillSpacing;
       }

     /**
     ** Returns the "thickness" of rectangular elements used to
     ** emulate any required non-rectangular features of the symbol.
     ** <p>
     **
     **
     ** @return the previously set (or the default, if the
     ** fillThickness has been set to <tt>GChart.NAI</tt>) fill
     ** thickness (in pixels).
     **
     ** @see #setFillThickness setFillThickness
     ** @see #setFillSpacing setFillSpacing
      */
       public int getFillThickness() {
         if (fillThickness==GChart.NAI)
           return symbolType.defaultFillThickness();
         else
           return fillThickness;
       }

      
  /**
   ** Returns the hovertextTemplate of this symbol.
   ** <p>
   **
   ** @return hovertextTemplate of the symbol
   **
   **
   ** @see #setHovertextTemplate(String) setHovertextTemplate
   **
   **/
       public String getHovertextTemplate() {
         if (null == hovertextTemplate)
           return symbolType.defaultHovertextTemplate();
         else
           return hovertextTemplate;
       }

     /**
      * Returns the URL that will be used for all of the
      * images used in rendering this symbol.
      * <p>
      *
      * @see #setImageURL setImageURL
      * @see #setBlankImageURL setBlankImageURL
      *
      * @return the url that defines the <tt>src</tt> property of all
      * images used to draw this symbol on the chart.
      */
     public String getImageURL() {
        String result = (null == imageURL) ?
                        getBlankImageURL() :
                        imageURL;
        return result;
     }
       // returns an internal, parsed form of the hovertext template
       HovertextChunk[] getHovertextChunks() {
          if (null == hovertextChunks)
             hovertextChunks =  HovertextChunk.parseHovertextTemplate(
                getHovertextTemplate());

          return hovertextChunks;
       }
      
      /** Returns the <tt>Curve</tt> that contains this
       ** <tt>Symbol</tt>.
       **
       ** @return a reference to the <tt>Curve</tt> that
       **   contains this <tt>Symbol</tt> (its "parent")
       **
       **/  
       public Curve getParent() {return parent;}
       /* returns the WidgetCursor of the curve that uses this Symbol*/
       WidgetCursor getParentWidgetCursor() {
        if (parent == null)
          return null;
        else
          return parent.getWidgetCursor();
      }
      
    /**
     ** Returns the value, previously specified via
     ** <tt>setPieSliceOrientation</tt>, that defines the angular
     ** orientation of any pie slices associated with this
     ** symbol.  <p>
     **
     **
     ** @return the value, either <tt>Double.NaN</tt> or a value
     **         between 0 and 1 previously set via
     **         <tt>setPieSliceOrientation</tt>, that determines the
     **         angular orientation of any pie slice associated
     **         with this symbol.
     **
     ** @see #setPieSliceOrientation setPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     **
      */
       public double getPieSliceOrientation() {
         return pieSliceOrientation;
       }
       // Used internally to translate <tt>Double.NaN</tt> into
       // an appropriate default slice orientation that, when pie
       // slice orientation isn't explicitly specified, results
       // in a series of adjacent slices that will form a pie
       // when the sum of the slice sizes equals 1.0
      
       double getDecodedPieSliceOrientation() {
         double result = pieSliceOrientation;
         if ((result!=result)) // x!=x is a faster isNaN
           result = getLastPieSliceOrientation();
         return result;
       }
      
    /**
     ** Returns the value, previously specified via
     ** <tt>setPieSliceSize</tt>, that defines the size of
     ** the angle subtended by any pie slice associated with this
     ** symbol.  <p>
     **
     ** @return the value, between 0 and 1 and previously set via
     **         <tt>setPieSliceSize</tt>, that defines the
     **         size of the "wedge of pie" as a fraction of
     **         the total pie, for any pie slice associated
     **         with this symbol.
     **
     ** @see #setPieSliceOrientation setPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     **
      */
       public double getPieSliceSize() {
          return pieSliceSize;
       }


    /*
     * Returns the radius of the pie from which this
     * symbol's pie slice was extracted.
     *
     */
     double getPieSliceRadius(PlotPanel pp, boolean onY2) {
         double w = getWidth(pp);      // needed to decode model
         double h = getHeight(pp,onY2);// width,height into pixels
         double result = (int) Math.round(Math.sqrt(w*w + h*h)/2.);
         return result;
       }

       // defines first, second edge angle in standard radian units
       double getPieSliceTheta0() {
         double result;
         result = (0.75 - getDecodedPieSliceOrientation())*2*Math.PI;
         return result;
       }
       double getPieSliceTheta1() {
         return getPieSliceTheta0() - 2.*Math.PI*getPieSliceSize();
       }

    /**
     ** Returns this symbol's height, as previously set by
     ** <tt>setHeight</tt>.
     **
     ** @return the previously set symbol height, in pixels.
     **
     ** @see #setHeight setHeight
      */
       public int getHeight() {
          return height;
       }
  /**
   ** Returns this symbol's height as previously set by
   ** <tt>setModelHeight</tt>.
   **
   ** @return the previously set symbol height, in model units
   **
   ** @see #setModelHeight setModelHeight
   ** @see #setModelWidth setWidth
   ** @see #setHeight setHeight
   ** @see #setWidth setWidth
   **
    */
     public double getModelHeight() {
        return modelHeight;
     }

    
     /**
     ** Returns this symbol's width as previously set by
     ** <tt>setModelWidth</tt>.
     **
     ** @return the previously set symbol width, in model units.
     **
     ** @see #setModelWidth setModelWidth
     ** @see #setModelHeight setModelHeight
     ** @see #setWidth setWidth
     ** @see #setHeight setHeight
     **
      */
       public double getModelWidth() {
          return modelWidth;
       }
  /** Returns this symbol's type.
    **
    ** @return the type of this symbol.
    ** @see #setSymbolType setSymbolType
    **
    **/
   public SymbolType getSymbolType() {
      return symbolType;
   }
  /**
   ** Returns this symbol's width
   ** as previously set by <tt>setWidth</tt>.
   **
   ** @return the previously set symbol width, in pixels
   **
   ** @see #setWidth setWidth
    */
     public int getWidth() {
        return width;
     }


  /**
   ** Specifies theCSS background color of all the rectangular
   ** elements used in rendering this symbol. For example, this
   ** would define the color of the non-border part of bars in a
   ** bar-chart, the color of the non-border part of each
   ** shading bar in a pie slice, etc.  <p>
   **
   ** You can use one of the 16 standard HTML/CSS color literals,
   ** illustrated below, to quickly specify common colors:
   **
   ** <p><ul>
   ** <img
   ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample10.png">
   ** </ul>
   **
   ** <p> For more variety, use a standard CSS RGB (red, green,
   ** and blue) color format such as "#FF0000" (same as "red"),
   ** "#00FF00" (same as "green"), "#0000FF" (same as "blue"),
   ** "#FFFFFF" (same as "white") or "#000000" (same as "black").
   ** 
   ** <p>
   ** The default symbol background color is
   ** <tt>DEFAULT_SYMBOL_BACKGROUND_COLOR</tt>
   **
   **
   ** @param backgroundColor standard CSS color-defining string
   **
   ** @see #getBackgroundColor getBackgroundColor
   ** @see #setBorderColor setBorderColor
   ** @see #DEFAULT_SYMBOL_BACKGROUND_COLOR DEFAULT_SYMBOL_BACKGROUND_COLOR
   **
   **/
     public void setBackgroundColor(String backgroundColor) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.backgroundColor = backgroundColor;
     }

     /** Specifies the baseline value for this symbol. Use a
      ** baseline value when you need to create bar charts whose
      ** bars extend up/down to a specified y baseline value (for
      ** vertical bar charts) or left/right to a specified x baseline
      ** value (for horizontal bar charts).
      **
      ** <p>
      **
      ** In greater detail:
      ** <p>
      ** <ul>
      **
      ** <li>For curves that employ symbol types with names of
      ** the form <tt>VBAR_BASELINE_*</tt>, a vertical bar is
      ** drawn that connects the x,y position of each data point
      ** to the horizontal line defined by the equation
      ** <tt>y=baseline</tt>.  For the default baseline setting
      ** of <tt>Double.NaN</tt>, the defining equation is
      ** <tt>y=(yMin+yMax)/2</tt> (i.e., a midpoint baseline).
      **
      ** <p> <li>For curves that employ symbol types with names
      ** of the form <tt>HBAR_BASELINE_*</tt>, a horizontal bar
      ** is drawn from the x,y position associated with each data
      ** point to the vertical line defined by the equation
      ** <tt>x=baseline</tt>. For the default baseline setting of
      ** <tt>Double.NaN</tt>, the defining equation is
      ** <tt>x=(xMin+xMax)/2</tt>.
      ** </ul>
      **
      ** <p>
      **
      ** @param baseline the y (or x) that defines the horizontal
      **   (or vertical) line to which any baseline-based vertical
      **   (or horizontal) bars are extended.
      **  
      ** @see #getBaseline getBaseline
      ** @see SymbolType#HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
      ** @see SymbolType#HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
      ** @see SymbolType#HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
      ** @see SymbolType#VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
      ** @see SymbolType#VBAR_BASELINE_EAST VBAR_BASELINE_EAST
      ** @see SymbolType#VBAR_BASELINE_WEST VBAR_BASELINE_WEST
      **
      **/
     public void setBaseline(double baseline) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.baseline = baseline;
     }

    
  /**
   ** Specifies the CSS border color of all the rectangular
   ** elements used in rendering this symbol.
   **
   ** <p>
   **
   ** For example, for a square symbol, this would set the color
   ** of the line that indicates the outter perimeter of that
   ** square.  However, for a pie slice, this would set the color
   ** of the outter perimeter of every shading bar used to fill
   ** in the pie slice.
   **
   ** <p>
   **
   ** <p> <i>Tip:</i> Although you can use the special CSS
   ** keyword "transparent", different GWT-supported browsers
   ** have different rules for deciding what color is "behind" an
   ** element's border, and thus they can sometimes render
   ** "transparent" differently. The easiest way to assure
   ** consistent border colors across all GWT-supported browsers
   ** is to use explicit colors, rather than "transparent", for
   ** borders whenever you can. For example, if you know that the
   ** plot area's background is gray, set the border to "gray" to
   ** obtain approximately the same result as if the border were
   ** "transparent".
   **
   ** @param borderColor the color of the
   ** border around each rectangular element used to render the
   ** symbol, in the standard CSS color format.
   **
   ** For more information on standard CSS color specifications see
   ** {@link Symbol#setBackgroundColor
   **        setBackgroundColor}.
   **
   ** @see #getBorderColor getBorderColor
   **
   **/
     public void setBorderColor(String borderColor) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.borderColor = borderColor;
     }
    
  /**
   ** Sets the border style of the rectangular elements used
   ** to render this symbol.
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getBorderStyle getBorderStyle
   ** @see #setBackgroundColor setBackgroundColor
   ** @see #setBorderColor setBorderColor
   **/
     public void setBorderStyle(String borderStyle) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.borderStyle = borderStyle;
     }
  /**
   ** Sets the width of the border around each rectangular
   ** element used to render this symbol's border, in pixels.
   **
   ** <p>
   ** If the symbol's width or height ever become less
   ** than twice the specified border width, the border will
   ** be shrunk down until it just fills up the entire
   ** rectangular area of the symbol.
   **
   ** @param borderWidth the width of the symbol's border, in pixels
   ** @see #getBorderWidth getBorderWidth
   **/
     public void setBorderWidth(int borderWidth) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.borderWidth = borderWidth;
     }

     /**
      ** Specifies if interpolated and filled-in rectangular
      ** elements, generated to render any required
      ** non-rectangular aspects of symbol, have hovertext added
      ** to them as defined by the symbol's hovertextTemplate.
      ** <p>
      **
      ** For example, in a line chart, the symbol has two
      ** aspects: a symbol representing the data point itself,
      ** and a series of "dots" that connect that point to any
      ** subsequent point. If this property is true, both the
      ** rectangular element representing the data point itself,
      ** as well as any interpolated "dots" have, say, a hovertext
      ** that indicates their x,y coordinates. If false, the
      ** elements representing the data points, but not the
      ** interpolated elements, get such hovertext.
      ** <p>
      **
      ** <blockquote><small> <i>Note:</i> In earlier versions,
      ** turning off fill hovertext made GChart updates faster.
      ** Because (as of GChart 2.1) hovertext generation is now
      ** deferred until mouse-overs, there's usually no longer a
      ** speed advantage to turning off hovertext, unless you
      ** have so many elements that hovering itself becomes a
      ** performance bottleneck. However, it may still save
      ** significant amounts of memory to turn off fill
      ** hovertext. In general, unless you want people to be able
      ** to hover over the filled-in points and see their values,
      ** it's best to disable fill hovertext.
      **
      ** </blockquote></small>
      **
      ** @param fillHasHovertext true (the default) if you want
      **    interpolated/filled-in elements to have hovertext,
      **    false if you don't want such hovertext.
      **        
      ** @see #getFillHasHovertext getFillHasHovertext
      ** @see #setHovertextTemplate setHovertextTemplate
      **
      **/
       public void setFillHasHovertext(boolean fillHasHovertext) {
          plotPanel.invalidateWidgetsAfterCursor(
            getParentWidgetCursor());
          this.fillHasHovertext = fillHasHovertext;
       }
    
     /**
     ** Specifies the spacing between successive rectangular
     ** elements used to render any required non-rectangular
     ** features of the symbol.  <p>
     **
     ** The exact meaning of this spacing setting depends on the
     ** symbol type:
     **
     ** <p>
     **
     ** <table border>
     **   <tr><th>SymbolType</th><th>How spacing is interpreted</th><th>Default value</th>
     **   </tr>
     **   <tr><td>BOX_*</td>
     **  
     **   <td>The distance between the centers of the "dots" used to
     **    draw the dotted connecting lines between successive
     **    x,y data points on a curve. </td> <p>
     **
     **   <td>4</td>
     **   </tr>
     **  
     **   <tr><td>LINE</td>
     **  
     **   <td>The horizontal distance between the centers of the
     **   successive vertical bars, or the vertical distance
     **   between the centers of the successive horizontal bars,
     **   that GChart uses to render the point-to-point
     **   connecting lines of the LINE symbol type.
     **   <p>
     **  
     **   The default of 1px provides the smoothest lines but
     **   also the longest chart update times. Larger values
     **   provide proportionally faster rendering of connecting
     **   lines (provided the connecting line segments are
     **   significantly longer than the specified spacing), but
     **   they give the lines a grainy, "stair-steppy" look.  <p>
     **  
     **   <i>Tip:</i> To assure an unbroken connecting line, use a
     **   fill thickness setting greater than or equal to your
     **   fill spacing setting.</td>
     **  
     **   <td>1</td>
     **   </tr>
     **  
     **   <tr><td>PIE_SLICE_*</td>
     **  
     **   <td>The vertical or horizontal distance between
     **   the centers of the vertical, and/or horizontal,
     **   shading bars used to fill in the pie slice</td>
     **   <td>4</td> </tr>
     **
     **   <tr>
      **   <td>VBAR_*</td>
     **
     **   <td>The horizontal distance between corresponding edges
     **      of the vertical bars used to fill in the trapezoidal
     **      areas linearly interpolated between successive
     **      vertical bars on a curve.  </td>
     **   
     **    <td>4</td>
     **   
     **   <tr>
     **   <td>HBAR_*</td>
     **
     **   <td>The vertical distance between corresponding edges
     **      of the horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive horizontal bars on a curve.  </td>
     **   
     **    <td>4</td>
     **   </tr>
     ** </table>  
     ** 
     ** <p>
     **
     ** Note that, if your goal is to produce a solid connecting
     ** line between points always use the <tt>LINE</tt> symbol
     ** type rather than the BOX_CENTER symbol type with a fill
     ** spacing of 1px. The <tt>LINE</tt> symbol type knows how
     ** to merge adjacent pixels into larger rectangular
     ** elements, and is therefore usually much more efficient,
     ** especially with curves that involve many near-vertical or
     ** near-horizontal connecting lines. For best performance,
     ** use the <tt>BOX_CENTER</tt> symbol only to produce
     ** dotted connecting lines that have widely spaced dots.
     ** <p>
     **
     ** In general, since the number of elements required is
     ** often inversely proportional to fill spacing, using a
     ** very small fill spacing like 1px, while allowed, could
     ** degrade performance unacceptably, especially for very
     ** large-sized charts. On the other hand, too large a fill
     ** spacing/thickness can degrade graphical quality
     ** unacceptably (e.g. due to too few "dots" on dotted
     ** connecting lines, "stair-steppy" solid connecting lines,
     ** or "grainy filled" pie slices).  <p>
     **
     ** <blockquote><small>
     **
     ** <i>Tip:</i> For pie slices as well as for dotted or solid
     ** connecting lines, scaling down the size of the chart via
     ** <tt>setXPixelSize</tt> and <tt>setYPixelSize</tt> can
     ** also speed up chart display, and thus will often provide
     ** a better-looking alternative to increasing the fill
     ** spacing.  <p>
     **
     **  In particular, for a typical curve whose x-values always
     **  increase with point index (i.e. no "doubling back")
     **  <tt>LINE</tt> symbol type curves often have a number of
     **  elements, and thus an update time, that is approximately
     **  equal to:
     ** 
     **   <p>
     **  
     **   <pre>
     **
     **     "Some Constant" * (xMaxInPixels-xMinInPixels)/fillSpacing
     **
     **   </pre> 
     **   <p>
     **
     **   So, for <tt>LINE</tt> curves, halving the x-axis range
     **   via <tt>setXPixelSize</tt> will provide approximately
     **   the same speedup as doubling the fill spacing setting,
     **   and, because the lines will be less "stair-steppy",
     **   will often provide a more acceptable visual result.
     **  
     **   </small>
     **   </blockquote>
     **
     ** Experience suggests that many applications will be able
     ** to find a combination of chart size and spacing/thickness
     ** settings that provide an acceptable level of both
     ** graphical quality and performance--particularly if your
     ** charting needs are more utilitarian than aesthetic.
     **
     ** <blockquote><small>
     ** <b>Understanding Client-side GChart's Limitations</b>
     ** <p>
     **
     ** Though GChart's "just say it with rectangles" approach
     ** greatly simplifies its implementation and consequently
     ** delivers many practical benefits (Examples: IE6 quirks
     ** just works; a simple bar chart with 10 bars can be
     ** updated in under 20ms; the entire package is implemented
     ** in under 2,000 semi-colon-terminated lines of GWT-Java so
     ** it shouldn't bulk up your application or be very hard for
     ** you to tweak should the need arise) it limits the visual
     ** quality and/or performance of charts that contain many
     ** data points or non-rectangular elements.  If the
     ** aesthetics of your site require more (sometimes only a
     ** 3-D pie chart with drop-shadows and all the trimmings
     ** will do), or you have charts that require over 500
     ** rectangular elements, you may have to use one
     ** of the many GWT-accessible, vector-graphics-based,
     ** charting packages such as jFreeChart, Google Chart API,
     ** Dojo Charting, PlotKit, timepedia's Chronoscope, YUI
     ** Charts, etc. Or consider a hybrid approach in which
     ** GChart annotations (via <tt>setAnnotationWidget</tt>) or
     ** image urls (via <tt>setImageURL</tt>) are used to
     ** precisely place, say, server-generated images onto an
     ** otherwise client-based GChart.
     ** <p>
     **
     ** Our intention is to push the visual quality/performance
     ** as far as practicable, but GChart's basic strategy has
     ** inherent limitations.
     **
     ** </small></blockquote>
     **
     ** @param fillSpacing spacing between successive rectangular
     **   elements used to fill in non-rectangular symbols, in
     **   pixels.
     **
     ** @see #getFillSpacing getFillSpacing
     ** @see #setFillThickness setFillThickness
     **
      */
     public void setFillSpacing(double fillSpacing) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
       // x!=x is a faster isNaN
       if (!(fillSpacing!=fillSpacing) && fillSpacing < 1)
         throw new IllegalArgumentException(
          "fillSpacing="+fillSpacing+"; "+
          "fillSpacing must either be >= 1, or else " +
          "equal to Double.NaN.");
       this.fillSpacing = fillSpacing;
     }

     /**
     ** Sets the "thickness" of the rectangular elements used to
     ** render any required non-rectangular features of this symbol.
     ** <p>
     **
     ** The exact meaning of this thickness setting, as well as
     ** the default used whenever the thickness is set to the
     ** special undefined integer value recognized by GChart
     ** (<tt>GChart.NAI</tt>), depends on the symbol type:
     ** <p>
     **
     ** <table border>
     **   <tr><th>SymbolType</th><th>How thickness is interpreted</th><th>Default value</th>
     **   </tr>
     **   <tr><td>BOX_*</td>
     **  
     **   <td>The height and width of rectangular "dots" used to
     **    draw the dotted connecting lines between successive
     **    x,y data points on a curve. </td> <p>
     **
     **   <td>0 (implies no interpolated dots)</td>
     **   </tr>
     **  
     **   <tr><td>LINE</td>
     **  
     **   <td>The width of the vertical line segments placed
     **   end-to-end to render any "more-nearly-vertical"
     **   connecting lines of the curve, and the height of the
     **   horizontal line segements placed end-to-end to render
     **   any "more-nearly-horizontal" connecting lines on this
     **   curve.  Note that if you use a fill thickness less than
     **   the fill spacing, your line will not be continuously
     **   connected.</td>
     **  
     **  <td>1</td>
     **   </tr>
     **  
     **   <tr><td>PIE_SLICE_*</td>
     **  
     **   <td>The width of vertical, and/or the height of
     **   horizontal, shading bars used to fill in the pie slice</td>
     **   <td>2</td>
     **   </tr>
     **
     **   <tr>
      **   <td>VBAR_*</td>
     **
     **   <td>The width of vertical bars
     **      used to fill in the trapezoidal areas linearly
     **      interpolated between successive vertical
     **      bars on a curve.  </td>
     **   
     **    <td>0 (implies no "area filling" between bars)
     **   
     **   <tr>
     **   <td>HBAR_*</td>
     **
     **   <td>The height of horizontal bars used to fill in the
     **      trapezoidal areas linearly interpolated between
     **      successive horizontal bars on a curve.  </td>
     **   
     **    <td>0 (implies no "area filling" between bars)
     **   </tr>
     ** </table>  
     **
     **
     **
     ** <p> This fill thickness setting and the associated fill
     ** spacing setting (c.f. <tt>setFillSpacing</tt>) work
     ** together to define the look and efficiency of pie slice
     ** shading, connecting lines, etc.
     **
     ** @param fillThickness the fill thickness, in pixels
     **
     ** @see #getFillThickness getFillThickness
     ** @see #setFillSpacing setFillSpacing
      */
       public void setFillThickness(int fillThickness) {
         plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
         if (fillThickness!=GChart.NAI && fillThickness < 0)
           throw new IllegalArgumentException(
            "fillThickness="+fillThickness+"; "+
            "fillThickness must either be >= 0, or else " +
            "equal to GChart.NAI.");
          this.fillThickness = fillThickness;
       }

  /**
   ** Defines the "hover-text" that appears whenever the user
   ** points their mouse at a point on the curve.
   **
   ** <p> Three special keywords, <tt>${x}</tt>, <tt>${y}</tt>, and
   ** <tt>${pieSliceSize}</tt>
   ** are supported within the hover-text String. Any
   ** occurrences of <tt>${x}</tt> in the string will be replaced with the
   ** x-coordinate of the point, formatted as per the specified
   ** tick label format of the x-axis. Any occurrences of
   ** <tt>${y}</tt> within the string will be replaced with the
   ** y-coordinate of the point, formatted either using the
   ** y-axis or y2-axis tick label format, depending on the axis
   ** on which the curve is displayed. Any occurances of
   ** <tt>${pieSliceSize}</tt> within the string will be replaced with
   ** 100 times the specified <tt>pieSliceSize</tt> of the point,
   ** formatted the same way as <tt>${y}</tt>, except that a "%" is
   ** tacked onto the end.
   ** <p>
   **
   ** <blockquote>
   **
   ** <i>Tip:</i> If the <tt>${</tt> is not followed by a
   ** recognized keyword and then by <tt>}</tt>, the "invalid
   ** keyword" passes through literally into the final hovertext
   ** (no exception is thrown). So, if you see keywords in your
   ** hovertext, it probably means you misspelled a keyword (e.g.
   ** you entered <tt>${piesliceSize}</tt> instead of
   ** <tt>${pieSliceSize}</tt>).
   **
   ** </blockquote>
   **
   **
   ** <p>
   **
   ** The default hovertext template, used automatically if
   ** hovertext template is <tt>null</tt>, is
   ** <tt>DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE</tt> for pie slice
   ** type symbols and <tt>DEFAULT_HOVERTEXT_TEMPLATE</tt> for
   ** all other symbol types.
   **
   ** <p> HTML is not supported within hover-text.  <p>
   **
   ** <blockquote><i>
   **
   ** The original keywords XXX and YYY of version 1.1, which
   ** were deprecated in version 2.0, have been dropped as of
   ** version 2.1. If you used these keywords, simply replace
   ** each XXX in your template text with <tt>${x}</tt>
   ** and each YYY with <tt>${y}</tt>. Support was
   ** dropped to simplify/speedup the code performing hovertext
   ** template parameter substitutions.
   **
   ** </i></blockquote>
   ** <p>
   **
   **
   ** @param hovertextTemplate defines the hoverText to display when the mouse
   **   moves over a point on this curve, with ${x}, ${y} and
   **   ${pieSliceSize} keywords replaced as described above.
   **
   ** @see #getHovertextTemplate getHovertextTemplate
   ** @see #setFillHasHovertext setFillHasHovertext
   ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
   ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
   **   DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
   **/
       public void setHovertextTemplate(String hovertextTemplate) {
          plotPanel.invalidateWidgetsAfterCursor(
             getParentWidgetCursor());
          if (this.hovertextTemplate != hovertextTemplate)
             hovertextChunks = null; // invalidate prev chunk-parse
          this.hovertextTemplate = hovertextTemplate;
       }
     /**
      * Specifies the URL that will define the image
      * used to represent the points on this curve.
      *
      * <p>
      *
      * Specify <tt>null</tt> to use the URL returned by
      * <tt>getBlankImageURL</tt> (this is the default,
      * and gives you a blank 1x1 pixel GIF).
      * <p>
      *
      * Most applications will do just fine with the default.
      * However, this method lets you replace the default,
      * rectangular, chart symbols with custom images (e.g. a
      * five pointed star) or even a Google Chart API url to use
      * tiny 3-D pie charts for each point symbol (it
      * looks a bit strange, and your chart will no longer
      * be strictly client-side any more, but it does work).
      * <p>
      *
      * Note that if the symbol's width and height are bigger or
      * smaller than the specified image, the image will be
      * stretched to fit the symbol's size. Except for single
      * pixel images and such, this does not usually look that
      * great, so exactly matching up the symbol and image size
      * is often best.
      *
      * <p>
      *
      * Be aware that GChart was originally designed with only
      * blank image URL's in mind, so it may take some effort to
      * adjust other settings (such as symbol type, width,
      * height, background color, border color, various legend
      * related settings, and curve order) so that the overall
      * chart looks right with your custom images for the curve
      * symbols. In particular, the legend icons are just
      * scaled-down versions of the image, which often doesn't
      * look that great.
      * <p>
      *
      * A alternative that gives you more control (but is less
      * efficient) is to use <tt>SymbolType.NONE</tt> with
      * <tt>setAnnotationWidget</tt> (or
      * <tt>setAnnotationText</tt>) and
      * <tt>setAnnotationLocation(AnnotationLocation.CENTER)</tt>
      * to use separate, centered, widget-based (or HTML based)
      * annotations in lieu of each point's image-based symbol.
      *
      *
      * @see #getImageURL getImageURL
      * @see #setBlankImageURL setBlankImageURL
      * @see GChart#setPlotAreaImageURL setPlotAreaImageURL
      * @see Curve.Point#setAnnotationWidget setAnnotationWidget
      * @see Curve.Point#setAnnotationText setAnnotationText
      * @see Curve.Point#setAnnotationLocation setAnnotationLocation
      * @see Symbol#setSymbolType setSymbolType
      * @see SymbolType#NONE SymbolType.NONE
      *
      * @param imageURL the url that defines the
      * image within all the rectangular elements used to draw
      * this symbol on the chart, or
      * <tt>null</tt> to revert to GChart's default (a 1x1 transparent
      * blank GIF).
      *
      */
     public void setImageURL(String imageURL) {
        this.imageURL = imageURL;
     }
    
  /**
   ** Sets the height of this symbol (including any specified border)
   ** in pixels.
   ** <p>
   **
   ** Symbols for drawing vertical bars and symbols defining
   ** vertical lines between points or across the entire chart,
   ** compute their heights automatically based on curve data,
   ** axes limits, specified baselines, etc. These symbols, namely
   ** <tt>XGRIDLINE</tt> and all those whose names begin with
   ** <tt>VBAR_</tt> will ignore this height setting.
   **
   ** <p>
   ** @param height height of this symbol, in pixels.
   **
   ** @see #getHeight getHeight
   **/
     public void setHeight(int height) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.height = height;
        this.modelHeight = Double.NaN;
     }
  /**
   ** Sets the height of this symbol (including any specified border)
   ** in model units (arbitrary, user-defined, units). Model
   ** units are the same units in which the points on the
   ** chart are specified and charted.
   ** <p>
   **
   ** Specification of the modelHeight undefines (that is, sets
   ** to <tt>GChart.NAI</tt>) any previous pixel-based
   ** specification made via <tt>setHeight</tt>.
   **
   ** <p> Symbols for drawing vertical bars and symbols defining
   ** vertical lines between points or across the entire chart,
   ** compute their heights automatically based on curve data,
   ** axes limits, specified baselines, etc. These symbols, namely
   ** <tt>XGRIDLINE</tt> and all those whose names begin with
   ** <tt>VBAR_</tt> will ignore this height setting.
   **
   ** <p>
   ** @param modelHeight height of this symbol, in model units
   **
   ** @see #getModelHeight getModelHeight
   ** @see #setHeight setHeight
   ** @see #setModelWidth setModelWidth
   ** @see #setWidth setWidth
   **/
     public void setModelHeight(double modelHeight) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.modelHeight = modelHeight;
     }
  /**
   ** Sets the width of this symbol (including any specified border)
   ** in model units. Model units are an arbitrary, user-defined
   ** units system associated with the x,y coordinates of
   ** points displayed on the chart.
   **
   ** <p> Specification of a symbol's model width undefines (that
   ** is, sets to <tt>GChart.NAI</tt>) any previous, pixel-based,
   ** width specification made via <tt>setWidth</tt><p>
   **
   ** Symbols for drawing horizontal bars, and symbols defining
   ** horizontal lines between points or across the entire chart,
   ** compute their widths automatically based on curve data,
   ** axes limits, specified baseline, etc. These symbols,
   ** namely <tt>YGRIDLINE</tt> and all those whose
   ** names begin with <tt>HBAR_</tt> will ignore this width
   ** setting.
   **
   ** <p>
   ** @param modelWidth width of this symbol, in model units.
   **
   ** @see #setModelHeight setModelHeight
   ** @see #setWidth setWidth
   ** @see #setHeight setHeight
   **
   **/
     public void setModelWidth(double modelWidth) {
        plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
        this.modelWidth = modelWidth;
     }

    /**
     ** Specifies a value that defines the angular orientation of
     ** the first edge of the pie slice associated with this
     ** symbol.  (An additional clockwise rotation as defined by
     ** <tt>setPieSliceSize</tt> defines the angular orientation
     ** of the second edge of the pie slice).
     **
     ** <p> When specified explicitly, the value must be a
     ** fraction >= 0 and < 1, with 0 representing due south,
     ** 0.25 an additional clockwise angular rotation (starting
     ** at due south) that is 25% of the full, 360 degree
     ** rotation (and thus, if you can follow these gyrations, is
     ** due west), 0.5 representing a 50% clockwise angular
     ** rotation from due south (thus, due north), .75 a 75%
     ** clockwise rotation (and thus, due east), etc.
     **
     ** <p> If the specially recognized value,
     ** <tt>Double.NaN</tt>, is specified, orientation is
     ** chosen so as to make this slice appear adjacent to
     ** the previous slice, (assuming it has the same x,y
     ** as the previous slice and is thus part of the same
     ** pie figure). If this symbol/point represents the
     ** very first pie slice, <tt>Double.NaN</tt>
     ** causes the slice to be oriented as specified via
     ** the <tt>setInitialPieSliceOrientation</tt> method
     ** (by default, that's due south).
     **
     ** Note that though this value can be set regardless of the
     ** symbol's <tt>SymbolType</tt>, it only has an impact on
     ** how the symbol is rendered if the symbol has one of the
     ** pie slice symbol types (e.g.
     ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
     **
     ** @param pieSliceOrientation angle at which first edge of pie
     ** slice appears, expressed as a fraction of a full
     ** 360 degree (2*Pi radians) clockwise rotation from an initial due
     ** south position (the 6 o'clock position) required to reach the first
     ** edge of the pie slice.
     **
     **
     **
     ** @see #getPieSliceOrientation getPieSliceOrientation
     ** @see #setPieSliceSize setPieSliceSize
     ** @see GChart#setInitialPieSliceOrientation setInitialPieSliceOrientation
     **
      */
     public void setPieSliceOrientation(
          double pieSliceOrientation) {
       plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
       if (pieSliceOrientation!=Double.NaN &&
           (pieSliceOrientation < 0 || pieSliceOrientation >=1))
         throw new IllegalArgumentException(
          "pieSliceOrientation="+pieSliceOrientation+"; "+
          "pieSliceOrientation must be >=0 and < 1, or else " +
          "equal to Double.NaN.");
       this.pieSliceOrientation = pieSliceOrientation;
     }

    /**
     ** Specifies a value that defines the angular size of
     ** any pie slice associated with this symbol.
     **
     ** <p> This must be value between 0 and 1.  0.25 represents
     ** a quarter pie slice, 0.5 a half pie, 1 a full pie, etc.
     **
     ** <p>
     **
     ** Note that though this value can be set regardless of the
     ** symbol's current <tt>SymbolType</tt>, it only has an
     ** impact on how the symbol is rendered if the symbol has
     ** one of the pie slice symbol types (e.g.
     ** <tt>PIE_SLICE_VERTICAL_SHADING</tt>).
     **
     ** @param pieSliceSize Fraction of a full pie subtended by
     **  this particular pie slice. Must be between 0 and 1,
     **  inclusive.
     **
     ** @see #getPieSliceSize getPieSliceSize
     ** @see #setPieSliceOrientation setPieSliceOrientation
     **
      */
     public void setPieSliceSize(
          double pieSliceSize) {
       plotPanel.invalidateWidgetsAfterCursor(
          getParentWidgetCursor());
       if (!withinRange(pieSliceSize,0,1))
         throw new IllegalArgumentException(
          "pieSliceSize="+pieSliceSize+"; the requirement: "+
          "0.0 <= pieSliceSize <= 1.0 must be satisfied.");
       this.pieSliceSize = pieSliceSize;
     }

    
  /** Sets the type of this symbol.
    **
    ** @param symbolType the new symbol type for this symbol.
    ** @see SymbolType SymbolType
    **
    */
   public void setSymbolType(SymbolType symbolType) {
      plotPanel.invalidateWidgetsAfterCursor(
           getParentWidgetCursor());
      this.symbolType = symbolType;
   }

  /**
   ** Sets the width of this symbol (including any specified border)
   ** in pixels.
   ** <p>
   **
   ** Symbols for drawing horizontal bars, and symbols defining
   ** horizontal lines between points or across the entire chart,
   ** compute their widths automatically based on curve data,
   ** axes limits, specified baseline, etc. These symbols, namely
   ** <tt>YGRIDLINE</tt> and all those whose names begin with
   ** <tt>HBAR_</tt> will ignore this width setting.
   **
   ** <p>
   ** @param width width of this symbol, in pixels
   **
   ** @see #setHeight setHeight
   **
   **/
     public void setWidth(int width) {
        plotPanel.invalidateWidgetsAfterCursor(
         getParentWidgetCursor());
       this.width = width;
       this.modelWidth = Double.NaN;
     }
     boolean borderColorNotSpecified() {
       return borderColor == null;
     }
     Annotation getAnnotation() {
       if (annotation == null) annotation = new Annotation();
       return annotation;
     }

     // Pixel height of symbol when rendered on given plot panel
     double getHeight(PlotPanel pp, boolean onY2) {
        double result;
        double mH = getModelHeight();
        if ((mH!=mH)) // x!=x is a faster isNaN
            result = getHeight();
        else
           result = pp.dyToPixel(mH,onY2);
        return result;
     }
     // Pixel width of symbol when rendered on given plot panel
     double getWidth(PlotPanel pp) {
        double result;
        double mW = getModelWidth();
        if ((mW!=mW)) // x!=x is a faster isNaN
            result = getWidth();
        else
           result = pp.dxToPixel(mW);
        return result;
     }

     /* Renders the symbol at the specified position within the
        plot panel, by creating appropriately positioned Image
        and Grid (for any Annotation associated with this symbol
        rendering) objects within the panel.  So-rendered symbols
        are used to represent: each point on a curve with any
        associated point annotations, axes, gridlines, ticks and
        their tick-labels. */
    
     void realizeSymbol(PlotPanel pp, Annotation annotation,
                         boolean onY2, boolean showOffChartPts,
                         double x, double y,
                         double prevX, double prevY,
                         double nextX, double nextY) {
       getSymbolType().realizeSymbol(pp, this, annotation,
                                     onY2,
                                     showOffChartPts,
                                     x, y,
                                     prevX, prevY, nextX, nextY);
    
     }
    
   } // end of class Symbol
  
  private static double lastPieSliceOrientation;
  static double getLastPieSliceOrientation() {
    return lastPieSliceOrientation;
  }
  static void setLastPieSliceOrientation(double lastOrientation) {
     lastPieSliceOrientation = lastOrientation%1.0;
  }
  private double initialPieSliceOrientation;
  /** Sets the default initial orientation for pie slices.
   **
   ** The default initial orientation is used as the first pie
   ** slice's first edge's orientation setting only if the symbol associated
   ** with that pie slice has the default, undefined, orientation
   ** setting of <tt>Double.NaN</tt>.
   ** <p>
   ** The default value of this setting is 0, which corresponds
   ** to due south (6 o'clock). The value specifies the
   ** fraction of a complete clockwise rotation, beginning
   ** at due south required to reach the first edge of the
   ** pie slice.
   **
   ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
   **
   ** @param orientation the orientation to use for the first
   **   edge of the first pie slice in this GChart, in cases
   **   in which that first pie slice's orientation is undefined
   **   (<tt>Double.NaN</tt>).
   **/
 
  public void setInitialPieSliceOrientation(double orientation) {
       plotPanel.invalidateWidgetsAfterCursor(
           plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
       if (orientation < 0 || orientation >=1)
         throw new IllegalArgumentException(
          "orientation="+orientation+"; "+
          "orientation must be >=0 and < 1.");
      this.initialPieSliceOrientation = orientation;
  }

  /**
   ** Returns a previously specified initial pie slice orientation.
   **
   ** @return the fraction of a clockwise rotation, beginning
   **   from the 6 o'clock postion, needed to reach the default
   **   initial pie slice orientation.
   **
   ** @see #setInitialPieSliceOrientation
   **   setInitialPieSliceOrientation
   **/
  public double getInitialPieSliceOrientation() {
    return initialPieSliceOrientation;
  }
   /**
    ** Specifies the type of symbol used by a curve. GChart
    ** includes a <tt>LINE</tt> symbol type (suitable for solidly
    ** connected line charts), various "box" symbol types
    ** (suitable for scatter and dotted-line charts),
    ** horizontal and vertical bars that extend to axis limits
    ** or a specified baseline (suitable for bar and area charts),
    ** and pie slices (suitable for pie charts) in these
    ** symbol types. Thus, choosing a curve's symbol type has a
    ** bigger impact on the kind of chart you create than in
    ** other charting APIs you may have used.
    **
    ** <p> One advantage of this symbol type based approach: you
    ** can place multiple pies, lines and/or bars on a single
    ** chart simply by creating multiple curves whose associated
    ** symbols have appropriately different symbol types.
    **
    ** <p> Note that, for line, area, or pie charts, the exact
    ** look of the non-rectangular aspects (connecting lines,
    ** filled-in areas, etc.) of these symbols in the chart is
    ** largely governed by the host <tt>Symbol</tt>'s
    ** <tt>fillSpacing</tt> and <tt>fillThickness</tt>
    ** properties.
    **
    ** <p> For instance, with the default <tt>fillThickness</tt>
    ** of 0 for the <tt>BOX_CENTER</tt> symbol, curves display
    ** only explicitly specified data points, without any
    ** connecting lines between them. But, if you set
    ** <tt>fillThickness</tt> to 1, GChart interpolates a series
    ** of 1 pixel by 1 pixel rectanglar "dots" between successive
    ** data points, with an intra-dot spacing defined by the
    ** symbol's <tt>fillSpacing</tt> setting.  <p>
    **
    ** You must select each curve's symbol type from the predefined
    ** set of supported types listed in the "Field Summary"
    ** section below. The default symbol type is <tt>BOX_CENTER</tt>.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setSymbolType setSymbolType
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    ** @see Symbol Symbol
    **
    **/
   public static class SymbolType {

      /*
       * If true, will draw symbol even if it's outside of the
       * plot area and showOffChartPoints is false for the
       * hosting GChart. Used for system-related curves (e.g.
       * those used to position the title, footnotes, etc.) that
       * must never be accidentally clipped off due to being
       * outside the plot area.
       *
       */
      protected boolean isAlwaysDrawn = false
     /*
      * This symbol type provides a convenient anchor point at
      * one of the standard 9 named positions within the plot
      * area.  The actual x,y of the points using this symbol
      * type is ignored.  Useful for placing annotations around
      * and along the perimeter of the plot panel.<p>
      *
      * For example, chart decorations such as axis labels and
      * footnotes internally use symbols of this type (with
      * appropriate setAnnotationXShift or setAnnotationYShift
      * adjustments to position the decoration appropriately
      * relative to the anchor point). End-users can use a curve
      * with this symbol type, along with a single point and
      * appropriate widget-based annotation, to place a table in
      * the upper left corner of the plot area, etc.
      *
      */
     private static class AnnotationAnchor extends SymbolType {
        AnnotationLocation location;
        AnnotationAnchor(AnnotationLocation location) {
          super(0, 0, 0, 0, 0, 0);
          this.location = location;
          isAlwaysDrawn = true;
        }
        // actual curve symbol zero-sized so it does not
        // appear--it's just for positioning the annotation.
        public double getAdjustedWidth(double width,
                                        double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return 0;
        }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                         double yMin, double yMax,double yMid) {
           return 0;
         }
        
         // Just return one of the standard 9 positions, based on
         // the location defined in the constructor.
        double getUpperLeftX(double width, double x,
                               double xPrev, double xNext,
                             double xMin, double xMax, double xMid) {
           double result;
           if (AnnotationLocation.NORTHWEST == location ||
               AnnotationLocation.WEST == location ||
               AnnotationLocation.SOUTHWEST == location)
              result = xMin;
           else if (AnnotationLocation.NORTHEAST == location ||
                    AnnotationLocation.EAST == location ||
                    AnnotationLocation.SOUTHEAST == location)
              result = xMax;
           else // NORTH, CENTER, or SOUTH
              result = (xMin + xMax)/2;
          
           return result;
          
      }       
         double getUpperLeftY(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid)  {
           double result;
           if (AnnotationLocation.NORTHWEST == location ||
               AnnotationLocation.NORTH == location ||
               AnnotationLocation.NORTHEAST == location)
              result = yMin;
           else if (AnnotationLocation.SOUTHWEST == location ||
                    AnnotationLocation.SOUTH == location ||
                    AnnotationLocation.SOUTHEAST == location)
              result = yMax;
           else // WEST, CENTER, or EAST
              result = (yMin + yMax)/2;
           return result;
         }
     }
     
     private static class HBarBaseline extends SymbolType {
         HBarBaseline(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0, 0);
         }
         public double getAdjustedWidth(double width,
                                        double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return x - xMid;
         }

      double getUpperLeftX(double width, double x,
                               double xPrev, double xNext,
                               double xMin, double xMax, double xMid) {
         return xMid;
      }  
        
        
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

     } // end of class HBarBaseline
     private static class HBarLeft extends SymbolType {
         HBarLeft(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5);
         }
         public double getAdjustedWidth(double width,
                                     double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return x - xMin;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

     } // end of class HBarLeft
    
      private static class HBarRight extends SymbolType {
         HBarRight(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5);
         }
        
         public double getAdjustedWidth(double width, double x,
                                        double xPrev, double xNext,
                                        double xMin, double xMax,
                                        double xMid) {
            return xMax - x;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }     
      } // end of class HBarRight


    // draws a connected straight line between successive points
    private static class LineSymbolType extends SymbolType {
         LineSymbolType() {
// same constructor as BOX_CENTER, which centers line segments on
// the points that they represent, as required.
             super(0, 0, 0, 0, 0, 0);
         }

     // fillSpacing to use when a symbol's fillSpacing is
     // set to GChart.NAI (an undefined integer)
      protected double defaultFillSpacing() {
        return DEFAULT_LINE_FILL_SPACING;
      }
     // fillThickness to use when a symbol's fillThickness is
     // set to GChart.NAI (an undefined integer)
      protected int defaultFillThickness() {
        return DEFAULT_LINE_FILL_THICKNESS;
      }
      int getIconHeight(int legendFontSize) {
            return 3; // leaves room for a 1px border and a center
      }
      int getIconWidth(int legendFontSize) {
          return Math.max(3, legendFontSize);
      }     
     // draws an approximate line from x,y to nextX, nextY, using an
     // appropriate series of vertical (for a > 45 degree slope) or
     // (for a < 45 degree slope) horizontal line segments.
     void realizeSymbol(PlotPanel pp, Symbol symbol,
                        Annotation annotation,
                        boolean onY2, boolean showOffChartPts,
                        double x, double y,
                        double prevX, double prevY,
                        double nextX, double nextY) {
       double xPx = pp.xToPixel(x);
       double yPx = pp.yToPixel(y, onY2);
       double nextXPx = pp.xToPixel(nextX);
       double nextYPx = pp.yToPixel(nextY, onY2);
       int thickness = symbol.getFillThickness();
       // draw line connecting to next point
       if (thickness > 0 &&
           !(x!=x) && !(y!=y) && // x!=x is a faster isNaN
           !(nextX!=nextX) && !(nextY!=nextY) &&
            (x!=nextX || y!=nextY)) {
         // setter constraint assures fill spacing is always >= 1
         double spacing = symbol.getFillSpacing();
         double deltaX = nextXPx - xPx;
         double deltaY = nextYPx - yPx;
         boolean dXIsShorter = deltaX*deltaX < deltaY*deltaY;
         // increasing width by 1 adds half px on each edge
         // to heal the occasional roundoff-induced gap
         final double EPS = 1;
         if (deltaX == 0) { // special-case of vertical line

             realizeOneImageOfSymbol(pp, symbol, null,
                                      onY2, showOffChartPts,
                    symbol.getFilledElementHovertextChunks(),
                                     0.5*(x+nextX),
                                     0.5*(y+nextY),
                                     xPx,
                                     0.5*(yPx+nextYPx),
                                     Double.NaN, Double.NaN,
                                     nextXPx, nextYPx,
                                     thickness,
                                     Math.abs(nextYPx - yPx)+EPS);
         }
         else if (deltaY == 0) { // special case of horizontal line

             realizeOneImageOfSymbol(pp, symbol, null,
                                      onY2, showOffChartPts,
                    symbol.getFilledElementHovertextChunks(),
                                     0.5*(x+nextX),
                                     0.5*(y+nextY),
                                     0.5*(xPx+nextXPx),
                                     yPx,
                                     Double.NaN, Double.NaN,
                                     nextXPx, nextYPx,
                                     Math.abs(nextXPx - xPx)+EPS,
                                     thickness);
         }
         else if (dXIsShorter) { // series of vertical segments
           double xMin = (xPx < nextXPx) ? xPx : nextXPx;
           double xMax = (xPx > nextXPx) ? xPx : nextXPx;
           double yAtXMin = (xPx < nextXPx) ? yPx : nextYPx;
           double yAtXMax =(xPx > nextXPx) ? yPx : nextYPx;

           double xiPrev = xMin;
           double yiPrev = yAtXMin;
           double xi = xiPrev;
           double yi = yiPrev;
           // round up to err on side of providing more detail
           int N = (int) Math.ceil((xMax-xMin)/spacing);
           double dy = Math.abs((yAtXMax - yAtXMin)/N)+EPS;
           for (int i = 1; i <= N; i++) {
             xi = xMin + i*(xMax - xMin)/N;
             yi = yAtXMin + i * (yAtXMax - yAtXMin)/N;
             realizeOneImageOfSymbol(pp, symbol, null,
                    onY2, showOffChartPts,
                    symbol.getFilledElementHovertextChunks(),
                    pp.xPixelToX(0.5*(xiPrev + xi)),
                    pp.yPixelToY(0.5*(yiPrev + yi),onY2),
                    0.5*(xiPrev+xi), 0.5*(yiPrev+yi),
                    Double.NaN, Double.NaN,
                    nextXPx, nextYPx,
                    thickness, dy);
             xiPrev = xi;
             yiPrev = yi;
            }
         }
         else { // dY is shorter. Series of horizontal segments
           double yMin = (yPx < nextYPx) ? yPx : nextYPx;
           double yMax = (yPx > nextYPx) ? yPx : nextYPx;
           double xAtYMin = (yPx < nextYPx) ? xPx : nextXPx;
           double xAtYMax = (yPx > nextYPx) ? xPx : nextXPx;

           double xiPrev = xAtYMin;
           double yiPrev = yMin;
           double xi = xiPrev;
           double yi = yiPrev;
           int N = (int) Math.ceil((yMax-yMin)/spacing);
           double dx = Math.abs((xAtYMax - xAtYMin)/N)+ EPS;
           for (int i = 1; i <= N; i++) {
             yi = yMin + i*(yMax - yMin)/N;
             xi = xAtYMin + i * (xAtYMax - xAtYMin)/N;
             realizeOneImageOfSymbol(pp, symbol, null,
                    onY2, showOffChartPts,
                    symbol.getFilledElementHovertextChunks(),
                    pp.xPixelToX(0.5*(xiPrev + xi)),
                    pp.yPixelToY(0.5*(yiPrev + yi),onY2),
                    0.5*(xiPrev+xi),0.5*(yiPrev+yi),
                    Double.NaN, Double.NaN,
                    nextXPx, nextYPx,
                    dx, thickness);
             xiPrev = xi;
             yiPrev = yi;
            }
         }
       }
       // the "main" symbol (the one on the (x,y) point itself) is
       // rendered last to put it on top of interpolated images
       double w = symbol.getWidth(pp);
       double h = symbol.getHeight(pp, onY2);
       realizeOneImageOfSymbol(pp, symbol, annotation,
                                 onY2, showOffChartPts,
                                 symbol.getHovertextChunks(),
                                 x,y,
                                 xPx, yPx,
                                 Double.NaN, Double.NaN,
                                 nextXPx, nextYPx,
                                 w, h);
     } // realizeSymbol

   }
      /* Symbols that are assigned this symbol type can be used
       * to represent a pie chart slice.
       *
       * The pivot point (center of containing pie) is at the x,y
       * location of the point. Typically, only a single point
       * per pie-slice curve is used (multiple points simply
       * translate the same pie slice symbol to another position,
       * such behaviour is useful if you want to use
       * a pie slice as a traditional curve symbol, but
       * it isn't needed for a typical pie chart).
       * <p>
       *
       * The initial angle and angle subtended by the slice are
       * specified by the <tt>pieSliceOrientation</tt> and
       * <tt>pieSliceSize</tt> properties of the host
       * <tt>Symbol</tt> (these properties only have meaning with
       * pie slice symbol types). Typically, several curves share
       * a common pie center point (x,y) and have orientations
       * and sizes that are coordinated so that the slices fit
       * together to form a single complete pie. GChart
       * facilitates this by choosing (by default) the next
       * slice's orientation so that it is adjacent to the
       * preceeding slice. However, other useful idioms include,
       * for example, adjusting the x,y pivots to produce
       * "exploded pie charts", or using a single slice that
       * fills up the entire pie as a disc-like alternative to
       * <tt>BOX_CENTER</tt>.  <p>
       *
       * The radius of the slice is chosen as the radius such
       * that the rectangle defined by the hosting Symbol's width
       * and height just barely fits within a circle with that
       * radius. This convention allows users to define the
       * pie radius in terms of the x model coordinates, y model
       * coordinates, or in pixels, as desired.
       * <p>
       *
       * The host <tt>Symbol</tt>'s fillSpacing and fillThickness
       * properties, along with horizontallyShaded and
       * verticallyShaded properties of this SymbolType, govern
       * how the slice is filled in.
       *
       * For more information with example code, see the
       * discussion under the {@link #PIE_SLICE_OPTIMAL_SHADING
       * PIE_SLICE_OPTIMAL_SHADING} symbol type.
       *
       */
     
      private static class PieSliceSymbolType extends SymbolType {
        private boolean horizontallyShaded;
        private boolean verticallyShaded;
        private boolean optimallyShaded;
       
        PieSliceSymbolType(boolean horizontallyShaded,
                           boolean verticallyShaded,
                           boolean optimallyShaded,
                           double pixelPadLeft,
                           double pixelPadRight,
                           double pixelPadTop,
                           double pixelPadBottom) {
          // same as BOX_SOUTHEAST (allows shading bars to be
          // easily positions by their upper left corners):
           super(1,1, pixelPadLeft, pixelPadRight,
                 pixelPadTop, pixelPadBottom);
            
          this.horizontallyShaded = horizontallyShaded;
          this.verticallyShaded = verticallyShaded;
          this.optimallyShaded = optimallyShaded;
        }

        protected double defaultFillSpacing() {
          return DEFAULT_PIE_SLICE_FILL_SPACING;
        }
        protected int defaultFillThickness() {
          return DEFAULT_PIE_SLICE_FILL_THICKNESS;
        }
        protected String defaultHovertextTemplate() {
          return DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE;
        }
       

        // min/max x (cosine) and y (sine) over "unit circle slice"
        private static class SliceLimits {
           double xMin;
           double xMax;
           double yMin;
           double yMax;
        }
       
        // Gets min/max sin, cos over slice cut from unit circle
        private SliceLimits getSliceLimits(double tMin,
                                           double tMax) {
          SliceLimits result = new SliceLimits();
          double xMin = 0; // origin of 0,0 present in every slice
          double xMax = 0; // (it's the pie center/slice pivot point)
          double yMin = 0;
          double yMax = 0;
          double tmp = 0;
          // points where each edge intersects the arc could be
          // extremal points--include them too.
          tmp = Math.cos(tMin);
          xMin = (xMin < tmp) ? xMin : tmp;   
          xMax = (xMax > tmp) ? xMax : tmp;
          tmp = Math.sin(tMin);
          yMin = (yMin < tmp) ? yMin : tmp;   
          yMax = (yMax > tmp) ? yMax : tmp;   

          tmp = Math.cos(tMax);
          xMin = (xMin < tmp) ? xMin : tmp;   
          xMax = (xMax > tmp) ? xMax : tmp;
          tmp = Math.sin(tMax);
          yMin = (yMin < tmp) ? yMin : tmp;   
          yMax = (yMax > tmp) ? yMax : tmp;

          // finally if slice includes any special extreme points
          // on the arc (namely, points of the arc that are
          // either due north, due south, due east or due west)
          // include those points in determining the min/max x
          // and min/max y included in the slice:
          double halfPi = Math.PI/2.;
          for (int i = (int) Math.ceil(tMin/halfPi);
               i*halfPi < tMax; i++) {
            double t = i*halfPi;
            tmp =  Math.cos(t);
            xMin = (xMin < tmp) ? xMin : tmp;   
            xMax = (xMax > tmp) ? xMax : tmp;
            tmp = Math.sin(t);
            yMin = (yMin < tmp) ? yMin : tmp;   
            yMax = (yMax > tmp) ? yMax : tmp;
          }

          result.xMin = xMin;
          result.xMax = xMax;
          result.yMin = yMin;
          result.yMax = yMax;

          return result;

        }

       
        // returns the y coordinate where a pie slice edge
        // intersects a given vertical line, or NaN if none.
        private static double yWherePieEdgeIntersectsVerticalLine(
          double xOfVerticalLine,
          double xPieCenter, double yPieCenter,
          double pieRadius, double pieEdgeAngle) {
          double result = Double.NaN;
          double dxToArc = pieRadius*Math.cos(pieEdgeAngle)
          if (dxToArc != 0) {
// The fraction of the way (from pie center to pie perimeter
// along the pie slice edge) that you must go to reach the point
// at which the vertical line intersects with the pie slice
// edge. For example, this fraction is 0.5 whenever the vertical
// line bisects the pie slice edge. 
            double t = (xOfVerticalLine-xPieCenter)/dxToArc;
            if (GChart.withinRange(t,0,1)) {
              result = yPieCenter -
                       t * pieRadius * Math.sin(pieEdgeAngle);
            }
          }
          return result;
        }

        // returns the x coordinate where a pie slice edge
        // intersects a given horizontal line, or NaN if none.
        private static double xWherePieEdgeIntersectsHorizontalLine(
          double yOfHorizontalLine,
          double xPieCenter, double yPieCenter,
          double pieRadius, double pieEdgeAngle) {
          double result = Double.NaN;
          double dyToArc = pieRadius*Math.sin(pieEdgeAngle);
          if (dyToArc != 0) {
// The fraction of the way (from pie center to pie perimeter
// along the pie slice edge) that you must go to reach the point
// at which the horizontal line intersects with the pie slice
// edge. For example, this fraction is 0.5 whenever the horizontal
// line bisects the pie slice edge. 
            double t = (yPieCenter - yOfHorizontalLine)/dyToArc;
            if (GChart.withinRange(t,0,1)) {
              result = xPieCenter +
                       t * pieRadius * Math.cos(pieEdgeAngle);
            }
          }
          return result;
        }
        /*
         * Returns the angle of a line extending from (0,0) to
         * (x,y) in radians in the standard range, 0 to 2*Pi. For
         * example, a line pointing due east such as (1,0) would
         * return 0, one pointing due north such as (0,0.5) would
         * return Pi/2, one pointing due west such as (-4.13,0)
         * would return Pi and the point (1,1) returns Pi/4. 
         *
         */
        private static double angle(double x, double y) {
          double result = Double.NaN;
          if (x == 0) {
            if (y > 0)
              result = Math.PI/2.;
            else if (y < 0)
              result = 3*Math.PI/2.;
          }
          else if (x> 0 && y >= 0)
            result = Math.atan(y/x);
          else if (x<0 && y >= 0)
            result = Math.PI - Math.atan(-y/x);
          else if (x <0 && y < 0)
            result = Math.PI + Math.atan(y/x);
          else if (x > 0 && y < 0)
            result = 2*Math.PI- Math.atan(-y/x);

          return result;
        }

        // is the given angle between the two angles given?
        private static boolean angleInRange(double angle,
                                              double theta0,
                                              double theta1) {

           if (theta0 > theta1)
              return angleInRange(angle, theta1, theta0);
          // angle is in standard 0 to 2*Pi range, but thetas
          // can be "wrapped around" several negative
          // multiples of 2*Pi less than the standard range;
          // this loop brings angle into same range as thetas
          while (angle > theta1)
             angle -= 2*Math.PI;

          boolean result = GChart.withinRange(angle, theta0, theta1);
          return result;
        }
       
        void realizeSymbol(PlotPanel pp, Symbol symbol,
                                     Annotation annotation,
                                     boolean onY2, boolean showOffChartPts,
                                     double x, double y,
                                     double prevX, double prevY,
                                     double nextX, double nextY) {

          double xPx = pp.xToPixel(x);
          double yPx = pp.yToPixel(y, onY2);
          // x!=x is a faster isNaN 
          if (!(xPx!=xPx) && !(yPx!=yPx) &&
               (showOffChartPts || pp.pointInPlotArea(
                  (int) Math.round(xPx), (int) Math.round(yPx)))) {
           // if center point is on the chart, draw it:

            double prevXPx = pp.xToPixel(prevX);
            double prevYPx = pp.yToPixel(prevY, onY2);
            double nextXPx = pp.xToPixel(nextX);
            double nextYPx = pp.yToPixel(nextY, onY2);
            double r = symbol.getPieSliceRadius(pp, onY2);
            double theta0 = symbol.getPieSliceTheta0();
            double theta1 = symbol.getPieSliceTheta1();

            HovertextChunk[] htc =
                symbol.getFilledElementHovertextChunks();

            int nBands = (int) Math.round(r/symbol.getFillSpacing());
  // Tweak radius to assure it is an even multiple of the fill
  // spacing. Makes it possible to assure regular band spacing
  // across pie at the expense of less precise control of pie
  // size (regular band spacing makes it look much better).
            r = nBands * symbol.getFillSpacing();

            /* Holds positions at which the current vertical or
             * horizontal "gridline-like band" intersects the outter
             * perimeter of the current pie slice. These positions
             * are used to define the location and size of shading
             * bars required for each pie slice.
             *
             * Note: Although most pie slice perimeters are convex
             * and thus have perimeters that intersect a gridline
             * in at most two points, pie slices that take up more
             * than half of the entire pie have perimeters that
             * can (across their pacman-like mouth) intersect a
             * gridline at up to four points.
             *
             */
           final int MAX_PIE_SLICE_PERIMETER_INTERSECTIONS = 4;
           double[] p = new double[MAX_PIE_SLICE_PERIMETER_INTERSECTIONS];
           final double EPS = 0.5;
           SliceLimits sl = getSliceLimits(theta1, theta0);
           boolean optimalIsVertical =
             (sl.yMax - sl.yMin) > (sl.xMax - sl.xMin);
           final boolean SHOW_SLICE_BAND = true;

            // perform any vertical shading that may be required:
            if (nBands > 0 && (verticallyShaded ||
               (optimallyShaded && optimalIsVertical))) {
               for (int i = (int) Math.round(nBands*sl.xMin);
                    i < sl.xMax*nBands; i++) {
                int nP = 0;
                double dxPx = r*(i+0.5)/nBands;
                double dyPx = Math.sqrt(r*r - dxPx*dxPx);
                // x of vertical line bisecting the shading band
                double xi = xPx + dxPx;
                // y-positions where this band crosses circle perimeter
                double c1 = yPx - dyPx;
                double c2 = yPx + dyPx;
                // y-positions where this band crosses each slice edge
                double e1 = yWherePieEdgeIntersectsVerticalLine(
                  xi,xPx,yPx,r,theta0);
                double e2 = yWherePieEdgeIntersectsVerticalLine(
                  xi,xPx,yPx,r,theta1);
                // Exclude circle perimeter intercepts outside of
                // the slice.  Note: Pixel y coordinates used in
                // browser increase going down, but cartesian y
                // coordinates used in trig functions increase
                // going up, hence the sign-flipping on second arg
                // of angle function below.
                if (angleInRange(angle(xi-xPx,yPx-c1),theta0,theta1))
                  p[nP++] = c1;

                // intersection points sorted by increasing y within p[]
                if (e1 < e2) {
                  // x!=x is a faster isNaN  
                  if (!(e1!=e1)) p[nP++] = e1;
                  if (!(e2!=e2)) p[nP++] = e2;
                }
                else {
                  if (!(e2!=e2)) p[nP++] = e2;
                  if (!(e1!=e1)) p[nP++] = e1;
                }

                if (angleInRange(angle(xi-xPx, yPx-c2),theta0,theta1))
                  p[nP++] = c2;

                for (int j = 1; j < nP; j++) {
                  // logic below avoids drawing a line across the
                  // non-convex "pacman mouth" that occurs with any
                  // bigger-than-half-pie-sized slices, by
                  // requiring that a line drawn from the pie
                  // center to an interpolated point on each
                  // shading bar forms an angle in the slice's
                  // angular range. We use a point 30% rather than
                  // 50% of the way inbetween to avoid ever hitting the
                  // center of the pie (where angle is ambiguous).
                  //
                  // Note that, due to roundoff error, you cannot
                  // ALWAYS rely on the (mathematically correct)
                  // fact that problematic bars always connect p[1]
                  // and p[2].
                  if (Math.abs(theta0-theta1) <= Math.PI ||
                      angleInRange(angle(xi-xPx,
                                         yPx-(0.3*p[j]+0.7*p[j-1])),
                                   theta0,theta1)) {
                  // widening of EPS pixels on either side fills in
                  // tiny intra-slice gaps (that can otherwise appear
                  // due to roundoff) by making each bar a tad bigger.
                    realizeOneImageOfSymbol(pp, symbol, null,
                                            onY2, SHOW_SLICE_BAND,
                                            htc, x, y,
                                            xi-0.5*
                                            symbol.getFillThickness(),
                                            p[j-1]-EPS,
                                            prevXPx, prevYPx,
                                            nextXPx, nextYPx,
                                            symbol.getFillThickness(),
                                            p[j] - p[j-1] +2*EPS);
                  }
                }
              }
            }

            // Now do any required horizontal shading. This is
            // basically the same as the code for vertical shading
            // above (w appropriate transposition/adjustments).
            if (nBands > 0 && (horizontallyShaded ||
              (optimallyShaded && !optimalIsVertical))) {
               for (int i = (int) Math.round(-nBands*sl.yMax);
                    i < -nBands * sl.yMin; i++) {
                int nP = 0;
                double dyPx = r*(i+0.5)/nBands;
                double dxPx = Math.sqrt(r*r - dyPx*dyPx);
              // y of the horizontal line bisecting the shading band
                double yi = yPx + dyPx;

             // x-positions where this band crosses circle perimeter
                double c1 = xPx - dxPx;
                double c2 = xPx + dxPx;

                // x-positions where this band crosses each slice edge
                double e1 = xWherePieEdgeIntersectsHorizontalLine(
                  yi,xPx,yPx,r,theta0);
                double e2 = xWherePieEdgeIntersectsHorizontalLine(
                  yi,xPx,yPx,r,theta1);
                // exclude circle perimeter intercepts outside of
                // the slice
                if (angleInRange(angle(c1-xPx, yPx-yi),theta0,theta1))
                  p[nP++] = c1;

                // intersection points sorted by increasing x within p[]
                if (e1 < e2) {
                  // x!=x is a faster isNaN
                  if (!(e1!=e1)) p[nP++] = e1;
                  if (!(e2!=e2)) p[nP++] = e2;
                }
                else {
                  if (!(e2!=e2)) p[nP++] = e2;
                  if (!(e1!=e1)) p[nP++] = e1;
                }

                if (angleInRange(angle(c2-xPx, yPx-yi),theta0,theta1))
                  p[nP++] = c2;

                for (int j = 1; j < nP; j++) {
                  // c.f. comment on corresponding vertical code above. 
                  if (Math.abs(theta0-theta1) <= Math.PI ||
                      angleInRange(angle((0.3*p[j]+0.7*p[j-1])-xPx,
                                         yPx-yi),
                                   theta0,theta1)) {
                  // widening of EPS pixels on either side fills in
                  // tiny intra-slice gaps that can sometimes appear
                  // by making slices just a tad bigger.
                    realizeOneImageOfSymbol(pp, symbol, null,
                                            onY2, SHOW_SLICE_BAND,
                                            htc, x, y,
                                            p[j-1]-EPS,
                                            yi-0.5*
                                            symbol.getFillThickness(),
                                            prevXPx, prevYPx,
                                            nextXPx, nextYPx,
                                            p[j]-p[j-1] + 2*EPS,
                                            symbol.getFillThickness());
                  }
                }
              }
            }


          // if the image has an attached label, realize that
            if (annotation!=null &&
                (annotation.getText() != null ||
                 annotation.getWidget() != null) &&
                annotation.getVisible()) {
              // plus x-axis, for shifts, always corresponds to
              // outward pointing radius that bisects the slice,
              // with positive y axis, for shifts, at a 90 degree
              // counter-clockwise rotation from this x. Basic
              // trigonometry and this spec yeilds lines below.
              double thetaMid = (theta0+theta1)/2.;
              double dX = annotation.getXShift();
              double dY = annotation.getYShift();
              double sinTheta = Math.sin(thetaMid);
              double cosTheta = Math.cos(thetaMid);
              // note: pixel Y increases down but yShift & "trig Y"
              // increase going up, which explains dY sign reversal
              realizeAnnotation(pp,
                                annotation,
                                annotation.getPieLocation(thetaMid),
                                xPx+(r+dX)*cosTheta - dY*sinTheta,
                                yPx-(r+dX)*sinTheta - dY*cosTheta,
                                0, 0,
                                symbol, x, y,onY2);
            }
          }
        }
      } // end of class PieSliceSymbolType


     
      private static class VBarBottom extends SymbolType {
         VBarBottom(int wm, int hm) {
             super(wm, hm,0.5,0.5,0.5,0.5);
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return yMax - y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarBottom
      private static class VBarBaseline extends SymbolType {
         VBarBaseline(int wm, int hm) {
             super(wm, hm, 0, 0, 0.5, 0.5);
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                         double yMin, double yMax,double yMid) {
           return y - yMid;
         }
        
         double getUpperLeftY(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid)  {
             return yMid;
         }
        
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarBaseline
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve.
       **/
      private static class VBarTop extends SymbolType {
         VBarTop(int wm, int hm) {
             super(wm, hm, 0.5, 0.5, 0.5, 0.5);
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return y - yMin;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/2.);
         }
      } // end of class VBarTop
      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the plot area, and do not have a
       ** visible symbol.<p>
       **
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_CENTER =
         new AnnotationAnchor(AnnotationLocation.CENTER);
     
      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the right edge of the plot area, and
       ** do not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the right
       ** edge of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_EAST =
         new AnnotationAnchor(AnnotationLocation.EAST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the top edge of the plot area, and do
       ** not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the top edge of
       ** the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTH =
         new AnnotationAnchor(AnnotationLocation.NORTH);
      /**
       ** Points on curves with this symbol type are positioned
       ** at the upper right corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the upper right corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTHEAST =
         new AnnotationAnchor(AnnotationLocation.NORTHEAST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the upper left corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the upper left corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_NORTHWEST =
         new AnnotationAnchor(AnnotationLocation.NORTHWEST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the bottom edge of the plot area, and
       ** do not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the bottom edge
       ** of the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTH =
         new AnnotationAnchor(AnnotationLocation.SOUTH);


      /**
       ** Points on curves with this symbol type are positioned
       ** at the lower right corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the lower right corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTHEAST =
         new AnnotationAnchor(AnnotationLocation.SOUTHEAST);


      /**
       ** Points on curves with this symbol type are positioned
       ** at the lower left corner of the plot area, and do not
       ** have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the lower left corner of the
       ** plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_SOUTHWEST =
         new AnnotationAnchor(AnnotationLocation.SOUTHWEST);

      /**
       ** Points on curves with this symbol type are positioned
       ** at the center of the left edge of the plot area, and do
       ** not have a visible symbol.<p>
       **
       ** Use this symbol type, along with the
       ** <tt>setAnnotationLocation</tt>, <tt>setAnnotationXShift</tt> and
       ** <tt>setAnnotationYShift</tt> methods, to position
       ** annotations relative to the center of the left edge of
       ** the plot area.
       **
       ** @see Curve.Point#setAnnotationLocation setAnnotationLocation
       ** @see Curve.Point#setAnnotationXShift setAnnotationXShift
       ** @see Curve.Point#setAnnotationYShift setAnnotationYShift
       **
       **/
      public static SymbolType ANCHOR_WEST =
         new AnnotationAnchor(AnnotationLocation.WEST);

     /** Use rectangles horizontally and vertically centered
       ** on each point of the curve */
      public static SymbolType BOX_CENTER =
           new SymbolType(0,0,0,0,0,0);
      /** Use rectangles just to the right of, and
       ** vertically centered on, each point of the curve */
      public static SymbolType BOX_EAST =
           new SymbolType(1, 0, 0.5, -0.5, 0, 0);
      /** Use rectangles just above, and horizontally centered
       ** on, each point of the curve */
      public static SymbolType BOX_NORTH =
         new SymbolType(0, -1,0,0,-0.5,0.5);
     
      /** Use rectangles just above, and to the right of,
       ** each point of the curve */
      public static SymbolType BOX_NORTHEAST =
         new SymbolType(1, -1, 0.5,-0.5,-0.5,0.5);
     
      /** Use rectangles just above and to the left of,
       ** each point of the curve */
      public static SymbolType BOX_NORTHWEST =
          new SymbolType(-1, -1, -0.5, 0.5, -0.5, 0.5);

      /** Use rectangles just below, and horizontally centered
       ** on, each point of the curve */
      public static SymbolType BOX_SOUTH =
         new SymbolType(0, 1, 0, 0, 0.5, -0.5);

      /** Use rectangles just below, and to the right of,
       ** each point of the curve */
      public static SymbolType BOX_SOUTHEAST =
         new SymbolType(1, 1, 0.5, -0.5, 0.5, -0.5);

      /** Use rectangles just below, and to the left of,
       ** each point of the curve */
      public static SymbolType BOX_SOUTHWEST =
        new SymbolType(-1, 1, -0.5, 0.5, 0.5, -0.5);

      /** Use rectangles just to the left of, and vertically centered
       ** on, each point of the curve */
      public static SymbolType BOX_WEST =
        new SymbolType(-1, 0, -0.5, 0.5, 0, 0);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and that are
       ** vertically centered on the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       ** @see Symbol Symbol
       **
       **/
      public static SymbolType HBAR_BASELINE_CENTER =
        new HBarBaseline(0,0);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** bottom edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       ** @see Symbol Symbol
       **
       **/
      public static SymbolType HBAR_BASELINE_NORTH =
        new HBarBaseline(0,-1);
      /**
       ** Use horizontal bars that extend from the x,y position
       ** associated with each point, to the x position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** top edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType HBAR_BASELINE_SOUTH =
        new HBarBaseline(0,1);
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** centered on the point.
       **/
      public static SymbolType HBAR_EAST = new HBarRight(1,0);
      /**
       ** Draws a horizontal bar from each point to the x
       ** coordinate of the next point.
       **
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #VBAR_PREV HBAR_PREV
       ** @see #VBAR_NEXT HBAR_NEXT
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setFillSpacing setFillSpacing
       **
       **/     
      public static SymbolType HBAR_NEXT =
       new SymbolType(0,0, 0.5, 0.5, 0, 0) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return xNext - x;
         }
         public double getUpperLeftX(double width, double x,
                               double xPrev, double xNext,
                               double xMin, double xMax, double xMid) {
            double result = (xNext!=xNext)? Double.NaN : x;
            return result;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

      };
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** just above the point.
       **/
      public static SymbolType HBAR_NORTHEAST = new HBarRight(1,-1);


      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** just above point.
       **/
      public static SymbolType HBAR_NORTHWEST  = new HBarLeft(-1,-1);
      /**
       ** Draws a horizontal bar from each point to the x
       ** coordinate of the previous point.
       **
       ** <p>
       **
       ** <blockquote><small> As of version 2.11, GChart's
       ** <tt>LINE</tt> symbol type can be used to draw arbitrary
       ** angled connecting lines between points. And, when such
       ** lines are vertical or horizontal, they are
       ** automatically merged into a single element.  Thus,
       ** though there may be specialized applications that
       ** require this symbol and companion symbols like
       ** <tt>VBAR_PREV</tt>, most applications will make out
       ** better using the newer <tt>LINE</tt> symbol type.  Note
       ** also that the <tt>BOX_CENTER</tt> symbol type, with
       ** appropriate fill spacing and fill thickness settings,
       ** can be used to draw dotted connecting lines, if that is
       ** what you are looking for.  </small> </blockquote>
       **
       ** @see #BOX_CENTER BOX_CENTER
       ** @see #LINE LINE
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #VBAR_PREV HBAR_PREV
       ** @see #VBAR_NEXT HBAR_NEXT
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setFillSpacing setFillSpacing
       **
       **
       **/
      public static SymbolType HBAR_PREV =
         new SymbolType(0,0,0.5,0.5,0,0) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return xPrev - x;
         }
         public double getUpperLeftX(double width, double x,
                                  double xPrev, double xNext,
                                  double xMin, double xMax, double xMid) {
            // x!=x is a faster isNaN
            double result = (xPrev!=xPrev)? Double.NaN : x;
            return result;
         }
         int getIconHeight(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);}
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

      };
      /** Use horizontal bars that extend from the right y-axis
       ** to each point on the curve, and that are vertically
       ** just below the point.
       **/
      public static SymbolType HBAR_SOUTHEAST = new HBarRight(1,1);
      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** just below the point.
       **/
      public static SymbolType HBAR_SOUTHWEST  = new HBarLeft(-1,1);

      /** Use horizontal bars that extend from the left y-axis
       ** to each point on the curve, and that are vertically
       ** centered on the point.
       **/
      public static SymbolType HBAR_WEST  = new HBarLeft(-1,0);  


      /**
       ** This symbol type draws a continuous straight line between
       ** successive individual data points.
       ** <p>
       **
       ** Apart from this connecting line, the individual data
       ** points are displayed exactly as they would have been
       ** displayed via BOX_CENTER.  <p>
       **
       ** Produces a connecting line similar to what could be
       ** produced via BOX_CENTER with a fill spacing of 1px,
       ** except that it uses a more efficient representation
       ** that merges vertical or horizontal "dot blocks" into
       ** single HTML elements whenever possible.
       **
       ** <p>
       ** Hovertext messages for any such merged blocks reflect the
       ** position at the center of the vertical or horizontal block.
       ** <p>
       **
       ** To produce a line without showing the individual data
       ** points as separate rectangular symbols, set width and
       ** height to match your symbol's specified
       ** <tt>fillThickness</tt>.
       **
       ** @see #BOX_CENTER BOX_CENTER
       ** @see Symbol#setFillThickness setFillThickness
       **
       **
       **
       **/
      public static SymbolType LINE  = new LineSymbolType();  

      /**
       ** A symbol type that does not draw any main symbol. Use
       ** this symbol type for curves whose points exist solely
       ** for the purpose of positioning their associated
       ** annotations. Note that if <tt>fillThickness</tt> is
       ** non-zero, any connecting dots between the points will
       ** still be drawn.  <p>
       **
       ** Equivalent to using the <tt>BOX_CENTER</tt> symbol
       ** type, but with the host symbol's width and height both
       ** set to zero, so that no box symbol is ever visible.
       **
       ** @see #BOX_CENTER BOX_CENTER
       ** @see Symbol#setFillThickness setFillThickness
       **
       **/
      public static SymbolType NONE =
         new SymbolType(0, 0, 0, 0, 0, 0) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return 0;
         }
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return 0;
         }
         int getIconHeight(int legendFontSize) {
            return 0;
         }
         int getIconWidth(int legendFontSize) {
            return 0;
         }

      };
      /**
       ** Draws a pie slice whose area is shaded using horizontal
       ** bars.
       **
       ** <p>
       ** The vertical distance between corresponding edges of
       ** successive bars is governed by the symbol's fill
       ** spacing property; the height of each bar is defined by
       ** the symbol's fill thickness property; the border and
       ** background of each shading bar are defined by the
       ** symbol's border color, border width, border style, and background
       ** color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_HORIZONTAL_SHADING =
        new PieSliceSymbolType(true, false, false, 0, 0, 0, 0);
      /**
       ** Draws a pie slice whose area is shaded using vertical
       ** bars.
       ** <p>
       **
       ** The horizontal distance between corresponding edges of
       ** successive bars is governed
       ** by the symbol's fill spacing property; the width
       ** of each bar is defined by the symbol's fill thickness
       ** property; the border and background of each
       ** shading bar are defined by the symbol's border color,
       ** border width, and background color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_VERTICAL_SHADING =
        new PieSliceSymbolType(false, true, false, 0, 0, 0, 0);
      /**
       ** Draws a pie slice whose area is shaded using both vertical
       ** and horizontal bars, which produces a "cross-hatched"
       ** pattern.
       ** <p>
       **
       ** The distance between corresponding edges of successive
       ** bars is governed by the symbol's fill spacing
       ** property; the thickness of each bar is defined by the
       ** symbol's fill thickness property; the border and
       ** background of each shading bar are defined by the
       ** symbol's border color, border width, border style, and background
       ** color properties.
       **
       ** <p> The radius of the pie slice (length of the non-arc
       ** sides of the slice) is chosen such that a circle with
       ** this radius circumscribes the host <tt>Symbol</tt>'s
       ** width/height determined rectangle. The slice pivot point
       ** (i.e. pie center) is defined by each point's x,y position, and the
       ** orientation and size of the slice by the corresponding
       ** properties (see links below) of the host <tt>Symbol</tt>.
       **
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
       ** @see Symbol Symbol
       **
       **
       **/
      public static SymbolType PIE_SLICE_HATCHED_SHADING =
        new PieSliceSymbolType(true, true, false, 0, 0, 0, 0);
      /**
       ** Draw a pie slice whose area is shaded using either
       ** vertical bars or horizontal bars--whichever
       ** renders the slice more efficiently. Specifically, pie
       ** slices that are wider than they are tall use horizontal
       ** shading and pie slices that are taller than they are
       ** wide use vertical shading. These choices minimize the
       ** the number of shading bars (and thus memory and time)
       ** required to render the pie slice.
       **
       ** <p>
       **
       ** The distance between corresponding edges of successive
       ** bars is governed by the symbol's fill spacing property;
       ** the thickness of each bar is defined by the symbol's
       ** fill thickness property; the border and background of
       ** each shading bar are defined by the symbol's border
       ** color, border width, and background color properties.
       ** <p>
       **
       ** The pie slice radius is always determined by the
       ** formula:
       **
       ** <p>
       ** <blockquote>
       **   <pre>
       **   sqrt(symbolWidth^2+symbolHeight^2)/2
       **   </pre>
       ** </blockquote>
       **
       ** <p>
       ** Here <tt>symbolWidth</tt> and <tt>symbolHeight</tt> are the pie
       ** slice symbol's width and height, in pixels.
       ** <p>
       **
       ** Note that this formula implies that the pie slice
       ** radius is the one associated with the circle that
       ** circumscribes the symbol, that is, the smallest circle
       ** that is big enough to completely contain the symbol's
       ** width/height defined bounding rectangle. Equivalently,
       ** the length of the pie slice radius equals the half the
       ** length of the diagonal across the symbol's bounding
       ** rectangle.
       **
       ** <p>
       **
       ** To assure an integral number of shading bars and thus
       ** improve the visual look of the pie chart, GChart
       ** automatically rounds the radius to the nearest
       ** multiple of the specified <tt>fillSpacing</tt>.  For
       ** example, if the radius computed from the above formula
       ** were 96 pixels and the <tt>fillSpacing</tt> were 10
       ** pixels, GChart would actually use a radius of 100 pixels.
       **
       ** <p>
       **
       ** <i>Tip:</i> To produce a pie slice with a radius, r, set
       ** the symbol's height to 0, and its width to 2*r (or
       ** visa-versa). To specify the radius in pixels, use the
       ** symbol's <tt>setWidth</tt> and <tt>setHeight</tt>
       ** methods; to specify the radius in "model units" (which
       ** scale up or down with the chart dimensions) use
       ** <tt>setModelWidth</tt> and <tt>setModelHeight</tt> instead.
       **
       ** <p>
       **
       **
       ** <p>
       ** The slice pivot point (i.e. pie center)
       ** is defined by each point's x,y position, and the
       ** orientation and size of the slice by the
       ** <tt>setPieSliceOrientation</tt> and
       ** <tt>setPieSliceSize</tt> methods
       ** of the host <tt>Symbol</tt>.
       ** <p>
       **
       ** Creating a pie chart from such pie slices requires
       ** that you define a separate curve for each slice,
       ** as illustrated in the code below:
       **
       ** {@code.sample ..\..\..\..\..\..\gcharttestapp\src\com\googlecode\gchart\gcharttestapp\client\GChartExample09.java}
       **
       ** <p>
       **
       ** Which produces this: <p>
       **
       ** <img
       ** src="{@docRoot}/com/googlecode/gchart/client/doc-files/gchartexample09.png">
       **
       ** <p> Note how, because
       ** <tt>PIE_SLICE_OPTIMAL_SHADING</tt> was used, vertical
       ** or horizontal shading is automatically selected so as
       ** to minimize the number of shading bars in each slice.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setFillSpacing setFillSpacing
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setBorderColor setBorderColor
       ** @see Symbol#setBorderWidth setBorderWidth
       ** @see Symbol#setBackgroundColor setBackgroundColor
       ** @see Symbol#setPieSliceOrientation setPieSliceOrientation
       ** @see Symbol#setPieSliceSize setPieSliceSize
       ** @see Symbol#setWidth setWidth
       ** @see Symbol#setHeight setHeight
       ** @see Symbol#setModelWidth setModelWidth
       ** @see Symbol#setModelHeight setModelHeight
       ** @see Curve.Point#setX setX
       ** @see Curve.Point#setY setY
       ** @see #PIE_SLICE_VERTICAL_SHADING PIE_SLICE_VERTICAL_SHADING
       ** @see #PIE_SLICE_HORIZONTAL_SHADING PIE_SLICE_HORIZONTAL_SHADING
       ** @see #PIE_SLICE_HATCHED_SHADING PIE_SLICE_HATCHED_SHADING
       **
       **/
      public static SymbolType PIE_SLICE_OPTIMAL_SHADING =
        new PieSliceSymbolType(false, false, true, 0, 0, 0, 0);
     
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and that are
       ** horizontally centered on the data point.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_CENTER = new VBarBaseline(0,0);
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** right edge passes through the data point.
       **
       ** @see Symbol Symbol
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_WEST = new VBarBaseline(-1,0);
      /**
       ** Use vertical bars that extend from the x,y position
       ** associated with each point, to the y position defined
       ** by the host <tt>Symbol</tt>'s baseline property, and whose
       ** left edge passes through the data point.
       **
       ** @see Symbol#setBaseline setBaseline
       ** @see #HBAR_BASELINE_CENTER HBAR_BASELINE_CENTER
       ** @see #HBAR_BASELINE_SOUTH HBAR_BASELINE_SOUTH
       ** @see #HBAR_BASELINE_NORTH HBAR_BASELINE_NORTH
       ** @see #VBAR_BASELINE_CENTER VBAR_BASELINE_CENTER
       ** @see #VBAR_BASELINE_EAST VBAR_BASELINE_EAST
       ** @see #VBAR_BASELINE_WEST VBAR_BASELINE_WEST
       **
       **/
      public static SymbolType VBAR_BASELINE_EAST = new VBarBaseline(1,0);
      /**
       ** Draws a vertical bar from each point to the y
       ** coordinate of the next point.
       **
       **
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #HBAR_PREV HBAR_NEXT
       ** @see #VBAR_PREV HBAR_PREV
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setFillSpacing setFillSpacing
       **
       **/
      public static SymbolType VBAR_NEXT =
        new SymbolType(0,0, 0, 0, 0.5, 0.5) {
         public double getAdjustedHeight(double height,
                                      double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax,
                                      double yMid) {
            // x!=x is a faster isNaN
            return yNext - y;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid) {
            return y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);
         }

      };
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** centered on the point.
       **/
      public static SymbolType VBAR_NORTH = new VBarTop(0, -1);
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** to the right of the point.
       **/
      public static SymbolType VBAR_NORTHEAST = new VBarTop(1, -1);
     
      /** Use vertical bars that extend from the top of the chart
       ** to each point on the curve, and are horizontally
       ** to the left of the point.
       **/
      public static SymbolType VBAR_NORTHWEST = new VBarTop(-1, -1);
      /**
       ** Draws a vertical bar from each point to the y
       ** coordinate of the previous point.
       **
       ** @see #HBAR_PREV HBAR_PREV
       ** @see #HBAR_PREV HBAR_NEXT
       ** @see #VBAR_NEXT HBAR_NEXT
       ** @see Symbol#setFillThickness setFillThickness
       ** @see Symbol#setFillSpacing setFillSpacing
       **
       **/
     
      public static SymbolType VBAR_PREV =
         new SymbolType(0,0,0,0,0.5,0.5) {
         public double getAdjustedHeight(double height, double y,
                                      double yPrev, double yNext,
                                      double yMin, double yMax, double yMid) {
            return yPrev - y;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid) {
            return y;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return (int) Math.round(legendFontSize/4.);
         }

      };
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** centered on the point.
       **/
      public static SymbolType VBAR_SOUTH = new VBarBottom(0, 1);
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** to the right of the point.
       **/
      public static SymbolType VBAR_SOUTHEAST = new VBarBottom(1, 1);
     
      /** Use vertical bars that extend from the x-axis
       ** to each point on the curve, and that are horizontally
       ** to the left of the point.
       **/
      public static SymbolType VBAR_SOUTHWEST =
          new VBarBottom(-1, 1);
      /**
       ** Represents a single x-axis grid-line. You can use
       ** this symbol to draw a single vertical bar
       ** across the chart.
       **
       **/
      public static SymbolType XGRIDLINE =
         new SymbolType(0,0,0,0,0.5,0.5) {
         public double getAdjustedHeight(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid) {
            return yMax - yMin;
         }
         public double getUpperLeftY(double height, double y,
                                  double yPrev, double yNext,
                                  double yMin, double yMax, double yMid) {
            return yMin;
         }
         int getIconHeight(int legendFontSize) {
            return legendFontSize;
         }
         int getIconWidth(int legendFontSize) {
            return 1;
         }

      };
      /**
       ** Represents a single y-axis (or y2-axis) grid-line. You
       ** can use this symbol to draw a single horizontal line (or
       ** bar) across the chart, for example, to display an upper
       ** bound or control limit.
       **
       **/
      public static SymbolType YGRIDLINE =
         new SymbolType(0,0,0.5,0.5,0,0) {
         public double getAdjustedWidth(double width, double x,
                                     double xPrev, double xNext,
                                     double xMin, double xMax, double xMid) {
            return xMax - xMin;
         }
         public double getUpperLeftX(double width, double x,
                                  double xPrev, double xNext,
                                  double xMin, double xMax, double xMid) {
            return xMin;
         }
         int getIconHeight(int legendFontSize) {
            return 1;
         }
         int getIconWidth(int legendFontSize) {
            return legendFontSize;
         }

      };

      /**
       **  @deprecated
       **
       ** This symbol is the same as <tt>YGRIDLINE</tt> and
       ** was added by mistake in version 1.
       ** (the y-axis isn't defined by the symbol type, but
       **  rather by the curve's <tt>setYAxis</tt> method).
       ** <p>
       ** Please use <tt>YGRIDLINE</tt> instead.
       **
       ** @see #YGRIDLINE YGRIDLINE
       ** @see GChart.Curve#setYAxis setYAxis
       **
       **/
    
      public static SymbolType Y2GRIDLINE = YGRIDLINE;
     
      // play similar role as same-named fields of AnnotationLocation
      protected int heightMultiplier;
      protected int widthMultiplier;
      /*
       * If a symbol's left, right, top, or bottom edge
       * represents the x or y location associated with this
       * symbol, the corresponding pixel paddings are 0.
       *
       * If a postion 1/2 pixel to the right, left, below, or
       * above (respectively) those edges represents the position
       * of the x or y in question, the values are 0.5.
       *
       * Why is this needed? Because GChart uses 1 px gridlines
       * whose center represents the point associated with the
       * gridline, and to get a corresponding edge to perfectly
       * overlay a gridline when its associated position is
       * the same as that gridline, we need to associated the
       * position of the represented x or y coordinate not
       * with the symbol's box edge itself, but rather
       * with a position 1/2 px towards the center of the
       * symbol. If you don't specify an extra half pixel
       * for, say, a vertical bar, it won't align right
       * on top of gridlines when it has the same height
       * as the associated gridline.
       * <p>
       *
       * In effect, we deliberately add a 1/2 px error to
       * certain symbols so that they appear to line up
       * perfectly with the gridlines.
       *
       */
      protected double pixelPadLeft;
      protected double pixelPadRight;
      protected double pixelPadTop;
      protected double pixelPadBottom;
    
      // symbols are part of the internals of a GChart,
      // so only we should instantiate them.
      private SymbolType(int widthMultiplier,
                         int heightMultiplier,
                         double pixelPadLeft,
                         double pixelPadRight,
                         double pixelPadTop,
                         double pixelPadBottom) {
         validateMultipliers(widthMultiplier, heightMultiplier);
         this.widthMultiplier = widthMultiplier;
         this.heightMultiplier = heightMultiplier;
         this.pixelPadLeft = pixelPadLeft;
         this.pixelPadRight = pixelPadRight;
         this.pixelPadTop = pixelPadTop;
         this.pixelPadBottom = pixelPadBottom;     
      }

      double getAdjustedHeight(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid) {
         return height;
      }

      double getAdjustedWidth(double width, double x,
                                      double xPrev, double xNext,
                                      double xMin, double xMax, double xMid) {
         return width;
      }

    
      // width of border of symbol displayed in legend key
      int getIconBorderWidth(int legendFontSize,
                             double symBorderFraction) {
         int result = 0;
         if (symBorderFraction > 0) {
            result = (int) Math.max(1.0, Math.floor(
               symBorderFraction * Math.min(
                 getIconWidth(legendFontSize),
                 getIconHeight(legendFontSize))));
         }
        return result;
     }
      int getIconHeight(int legendFontSize) {
         return (int) Math.round(0.75*legendFontSize);
      }

      int getIconWidth(int legendFontSize) {
         return (int) Math.round(0.75*legendFontSize);
      }  

      double getUpperLeftX(double width, double x,
                               double xPrev, double xNext,
                               double xMin, double xMax, double xMid) {
         double adjWidth = getAdjustedWidth(width, x,
                                      xPrev, xNext, xMin, xMax, xMid);
         double result =
           x + (0.5*(widthMultiplier - 1)) * adjWidth;
         return result;
      }  

      double getUpperLeftY(double height, double y,
                                        double yPrev, double yNext,
                                        double yMin, double yMax, double yMid)  {
         double adjHeight = getAdjustedHeight(height, y,
                                          yPrev, yNext, yMin, yMax, yMid);
         double result =
           y + (0.5*(heightMultiplier - 1)) * adjHeight;
         return result;

      }  
     
     // fillSpacing to use when a symbol's fillSpacing is Double.NaN
      protected double defaultFillSpacing() {
        return DEFAULT_SYMBOL_FILL_SPACING;
      }
     // fillThickness to use when a symbol's fillThickness is
     // GChart.NAI
      protected int defaultFillThickness() {
        return DEFAULT_SYMBOL_FILL_THICKNESS;
      }
      // hovertextTemplate to use when hovertext is null
      protected String defaultHovertextTemplate() {
          return DEFAULT_HOVERTEXT_TEMPLATE;
        }
  /*
   * Creates an image that represents the given symbol (or
   * a rectangular piece of a compound-image symbol).
   *
   * Why we use the img tag instead of the more conventional div
   * tag for the rectangles: those parts of the CSS box mode
   * that GChart relies upon for cross-GWT-browser consistency
   * behave the same for 1px x 1px transparent GIF-based img tags
   * in all GWT browsers, but do not behave the same for div
   * tags.  Specifically, IE6-quirks does place the border
   * outside the main element area, as CSS says it should, for
   * img tags but not for div tags ("quirks mode" does not live
   * up to it's name for the image tag). And testing suggested
   * divs are no more efficient than images, all things
   * considered (divs were a bit faster, on average, but chewed
   * up a bit more memory, on average, so it seemed to be,
   * performance wise, a wash). I've had some real problems with
   * the darn URL to the 1x1 gif recently (including a nasty FF 2
   * memory leak that wouldn't have happened with divs), but on
   * balance, I think the img tags are OK for now.
   *
   */
   private ReusableAbsolutePanel.ReusableImage createReusableImage(
                             PlotPanel pp, Symbol symbol,
                             double width, double height,
                             int borderWidth,
                             double x, double y) {
     ReusableAbsolutePanel.ReusableImage result;
     result = pp.getNextOrNewImage(symbol.getBackgroundColor(),
                                        symbol.getBorderColor(),
                                        symbol.getBorderStyle(),
                                        borderWidth,
                                        width,
                                        height,
                                        x, y, symbol.getImageURL());
     return result;
   }

  /*
   * Unmanaged images. Supports older code that simply
   * zaps/recreates each image, relying on browser's garbage
   * collector to deal with the reuse issue (that's slower).
   *
   */
   private Image createImage(Symbol symbol,
                             double width, double height,
                             int borderWidth,
                             String url) {

     Image result = new Image(url);
     // if smaller of width, height is at least twice
     // the border width, border width is used as is, otherwise,
     // it's replaced with half the smaller of width, height:
     int cappedBW = (int)
       ((2*borderWidth <= ((width < height) ? width : height)) ?
        borderWidth : (((width < height) ? width : height)/2));                       
      // If border was too big to fit inside rectangle, since GChart
      // borders are uniform around the rectangle, odd-sized
      // dimensions can leave a single "leftover" 1px inside the
      // border. Set background to the border's color so that the
     // border, in effect, takes up the entire rectangle.
     String backgroundColor = (cappedBW == borderWidth) ?
                            symbol.getBackgroundColor() :
                            symbol.getBorderColor();
     GChart.setBackgroundColor(result, backgroundColor);
     GChart.setBorderColor(result, symbol.getBorderColor());
     GChart.setBorderStyle(result, symbol.getBorderStyle());
     GChart.setBorderWidth(result, cappedBW);
     result.setPixelSize((int) Math.round(width)-2*cappedBW,
                         (int) Math.round(height)-2*cappedBW);
     return result;
   }
     // creates small image of symbol (used in the chart legend).
   Image createIconImage(Symbol symbol,
                         int legendFontSize, double symBorderFraction) {
      Image result = createImage(symbol,
                         getIconWidth(legendFontSize),
                         getIconHeight(legendFontSize),
                         getIconBorderWidth(legendFontSize,
                                            symBorderFraction),
                         symbol.getImageURL());
        return result;
   }

   // are two one-dimensional ranges (x1...x2 and y1...y2) disjoint?
   private boolean areDisjointRanges(double x1, double x2,
                                     double y1, double y2) {
     boolean result = false;
     if ((x1 < y1 && x2 < y1 && x1 < y2 && x2 < y2) ||
         (y1 < x1 && y2 < x1 && y1 < x2 && y2 < x2))
       result = true;
     return result;
   }

   // do two rectangular regions intersect?
   boolean intersects(
     double left1, double top1, double right1, double bottom1,
     double left2, double top2, double right2, double bottom2) {
     boolean result = true;
     if (areDisjointRanges(left1, right1, left2, right2) ||
         areDisjointRanges(top1, bottom1, top2, bottom2))
       result = false;
     return result;
   }

   protected void realizeAnnotation(PlotPanel pp,
                                    Annotation annotation,
                                    AnnotationLocation loc,
                                    double xCenter,
                                    double yCenter,
                                    double symWidth,
                                    double symHeight,
                                    Symbol symbol,
                                    double hoverX,
                                    double hoverY,
                                    boolean onY2) {

     int annWidth = annotation.getWidthUpperBound();
     int annHeight = annotation.getHeightUpperBound();
     AlignedLabel alignedLabel = pp.getNextOrNewAlignedLabel(
        annotation.getFontSize(),
        annotation.getFontStyle(),
        annotation.getFontWeight(),
        annotation.getFontColor(),
        loc.getHorizontalAlignment(),
        loc.getVerticalAlignment(),
        symbol.getHovertextChunks(),
        hoverX, hoverY, onY2,
        symbol.getPieSliceSize(),       
        annotation.getText(), annotation.isHTML(),
        annotation.getWidget());
// If positioning by top or left edges, explicit sizing isn't needed
// (makes the bounding box tighter, which is a little faster).     
     if (loc.getHorizontalAlignment() !=
         HasHorizontalAlignment.ALIGN_LEFT)
        alignedLabel.setWidth(annWidth + "px");
     else
        alignedLabel.setWidth("");
       
     if (loc.getVerticalAlignment() !=
         HasVerticalAlignment.ALIGN_TOP)
         alignedLabel.setHeight(annHeight + "px");
     else
        alignedLabel.setHeight("");

     pp.setLabelPosition(alignedLabel,
        loc.getUpperLeftX(xCenter,
              annWidth, Math.abs(symWidth)),
        loc.getUpperLeftY(yCenter,
              annHeight, Math.abs(symHeight)));
   }

   /* renders a single image that is part of a (possibly
    * multi-image) symbol, along with that image's annotation */
   protected void realizeOneImageOfSymbol(PlotPanel pp,
                                        Symbol symbol,
                                        Annotation annotation,
                                        boolean onY2,
                                        boolean showOffChartPts,
                                        HovertextChunk[] htc, 
                                        double hoverX, double hoverY,
                                        double xPx, double yPx,
                                        double prevXPx, double prevYPx,
                                        double nextXPx, double nextYPx,
                                        double width, double height) {


        double xMin = pp.getXMin();
        double xMax = pp.getXMax();
        double xMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((xMid!=xMid)) xMid = (xMin + xMax)/2.;
        double xMinPx = pp.xToPixel(xMin);
        double xMaxPx = pp.xToPixel(xMax);
        double xMidPx = pp.xToPixel(xMid);
       
        double symWidth = getAdjustedWidth(width, xPx,
                                          prevXPx, nextXPx,
                                          xMinPx, xMaxPx, xMidPx);
        if ((symWidth!=symWidth)) return; // x!=x is a faster isNaN
       
        double xLeft = getUpperLeftX(width, xPx,
                                             prevXPx, nextXPx,
                                             xMinPx, xMaxPx, xMidPx);
        if ((xLeft!=xLeft)) return; // x!=x is a faster isNaN
       
        double xCenter = xLeft + symWidth/2.;
        // the data and pixel Y coordinates are flipped, hence
        // the (counter-intuitive) min/max interchange below:
        double yMin =  onY2?pp.getY2Max():pp.getYMax();
        double yMax =  onY2?pp.getY2Min():pp.getYMin();
        double yMid = symbol.getBaseline();
        // x!=x is a faster isNaN
        if ((yMid!=yMid)) yMid = (yMin + yMax)/2.;
        double yMinPx = pp.yToPixel(yMin,onY2);
        double yMaxPx = pp.yToPixel(yMax,onY2);
        double yMidPx = pp.yToPixel(yMid,onY2);
       
        double symHeight = getAdjustedHeight(
                                  height, yPx,
                                  prevYPx, nextYPx, yMinPx, yMaxPx, yMidPx);
        if ((symHeight!=symHeight)) return; // x!=x is a faster isNaN
       
        double yTop = getUpperLeftY(height, yPx,
                                            prevYPx, nextYPx,
                                            yMinPx, yMaxPx, yMidPx);
        if ((yTop!=yTop)) return; // x!=x is a faster isNaN
       
        double yCenter =  yTop + symHeight/2.;

        if (!showOffChartPts &&
            !intersects(xMinPx, yMinPx, xMaxPx, yMaxPx,
                        xLeft, yTop, xLeft+symWidth, yTop+symHeight))
          return; // image is completely off plot area, so skip it.


        // translate negative width, height to equivalent
        // positive values that image tags can handle
        int signWidth = 1;
        if (symWidth < 0) {
           xLeft = xLeft + symWidth;
           symWidth *= -1;
           signWidth = -1;
        }
        int signHeight = 1;
        if (symHeight < 0) {
           yTop = yTop + symHeight;
           symHeight *= -1;
           signHeight = -1;
        }

        // Positive pixel padding pushes the specified edge
        // outward from the center by the given amount, without
        // changing the location of the center the symbol.
        // Similarly, negative padding, pushes the edge inward.
       
        xLeft -= pixelPadLeft;
        yTop -= pixelPadTop;
        symWidth += pixelPadLeft + pixelPadRight;
        symHeight += pixelPadTop + pixelPadBottom;

        if (symWidth > 0 && symHeight > 0) {
          ReusableAbsolutePanel.ReusableImage symbolImage =
             createReusableImage(pp, symbol, symWidth, symHeight,
                         symbol.getBorderWidth(), xLeft, yTop);

          symbolImage.setHovertextInfo(htc, hoverX, hoverY, onY2,
                                       symbol.getPieSliceSize());
        }
        // if the image has an attached label, realize that
        if (annotation!=null &&
            (annotation.getText() != null ||
             annotation.getWidget() != null) &&
            annotation.getVisible()) {
           AnnotationLocation loc = AnnotationLocation.transform(
              annotation.getLocation(), signWidth, signHeight);
          // Note: yShift follows orientation of cartesian y
          // Axis, which is 180 degrees different from pixel y
          // coordinates, hence the extra "-" below.
          realizeAnnotation(pp, annotation, loc,
                            xCenter+annotation.getXShift(),
                            yCenter-annotation.getYShift(),
                            symWidth, symHeight,
                            symbol,
                            hoverX,
                            hoverY,
                            onY2);
        }
    
   }

     // Distance from the point (x1, y1) to the point (x2, y2)
     protected double distance(
         double x1, double y1, double x2, double y2) {
        double result = Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
        return result;
     }
     /* Renders the symbol at the specified position within the
        plot panel, by creating appropriately positioned Image
        and Label objects within the panel.  So-rendered symbols
        are used to represent: each point on a curve (including
        any "filled" elements linearly interpolated between
        successive points) axes, gridlines, ticks, and
        tick-labels. */
     void realizeSymbol(PlotPanel pp, Symbol symbol,
                        Annotation annotation,
                        boolean onY2, boolean showOffChartPts,
                        double x, double y,
                        double prevX, double prevY,
                        double nextX, double nextY) {
       double xPx = pp.xToPixel(x);
       double yPx = pp.yToPixel(y, onY2);
       double prevXPx = pp.xToPixel(prevX);
       double prevYPx = pp.yToPixel(prevY, onY2);
       double nextXPx = pp.xToPixel(nextX);
       double nextYPx = pp.yToPixel(nextY, onY2);
       // draw line connecting to next point (or, for bar charts,
       // draws the trapezoidal area stretching up to next bar)
       if (symbol.getFillThickness() > 0 &&
           !(x!=x) && !(y!=y) && // x!=x is a faster isNaN
           !(nextX!=nextX) && !(nextY!=nextY)) {
         double d = distance(xPx,yPx,nextXPx,nextYPx);
         int nChunks = (int) Math.round(d/symbol.getFillSpacing());
         if (nChunks > 1) {
            double deltaX = nextXPx - xPx;
            double deltaY = nextYPx - yPx;
            boolean dXIsLonger = deltaX*deltaX > deltaY*deltaY;
            if (dXIsLonger) {
               deltaY /= deltaX; // from now on, dy is really dy/dx
               deltaX /= nChunks;// from now on, dx is for 1 chunk
            }
            else {
               deltaX /= deltaY;  // from now on, dx is really dx/dy
               deltaY /= nChunks; // from now on, dy is for 1 chunk
            }
            double dxm = (nextX - x)/nChunks;
            double dym = (nextY - y)/nChunks;
          // i==0 corresponds to the (to-be-drawn-last) symbol on (x,y).
            for (int i = 1; i < nChunks; i++) {
              // linearly interpolate forwards towards the next
              // point; forward interpolation (usually) lets us
              // place the "main" symbol for the original point on
              // top of these interpolated symbols, in one pass.
              double xi;
              double yi;

              // Rounding to the longer dimension first, then
              // using that pixelated position to determine other
              // dimension tends to keep points closer to being
              // on the mathematically ideal line (at the cost of
              // being less evenly spaced along that line). It's
              // not too hard to see the improved alignment on
              // GChartExample03, for example.
              if (dXIsLonger) {
                 xi = Math.round(xPx + deltaX * i);
                 yi = Math.round(yPx + deltaY*(xi - xPx));
              }
              else { // delta y is longer
                 yi = Math.round(yPx + deltaY * i);
                 xi = Math.round(xPx + deltaX*(yi - yPx));
             }             

              // model x,y of interpolated pts (needed for hovertext)
              double xm = x + i*dxm;
              double ym = y + i*dym;

              // interpolated symbols set width & height to
              // thickness, but are otherwise the same as main
              // symbol at (x,y)
              realizeOneImageOfSymbol(pp, symbol, null,
                                      onY2,
                                      showOffChartPts || isAlwaysDrawn,
                                      symbol.getFilledElementHovertextChunks(),
                                      xm,ym,
                                      xi, yi,
                                      prevXPx, prevYPx,
                                      nextXPx, nextYPx,
                                      symbol.getFillThickness(),
                                      symbol.getFillThickness());
            }
         }
         // else points too close to require any "filler" points
       }
       // the "main" symbol (the one on the (x,y) point itself) is
       // rendered last to put it on top of interpolated images
       realizeOneImageOfSymbol(pp, symbol, annotation,
                               onY2,
                               showOffChartPts || isAlwaysDrawn,
                               symbol.getHovertextChunks(),
                               x,y,
                               xPx, yPx,
                               prevXPx, prevYPx,
                               nextXPx, nextYPx,
                               symbol.getWidth(pp),
                               symbol.getHeight(pp,onY2));
     }
    
   } // end of class SymbolType
  
  /** The x-axis of a GChart.
   *
   * @see GChart#getXAxis getXAxis
   */
 
  public class XAxis extends Axis {
     XAxis() {
        super();
        setTickSymbol(new XTickSymbol());
        setGridSymbol(new XGridSymbol());
     }
     public int getAxisLabelThickness() {
        final int EXTRA_CHARHEIGHT = 2; // 1-char space above & below
        final int DEF_CHARHEIGHT = 1;
        int result = 0;
        if (null == getAxisLabel())
           result = 0;
        else if (GChart.NAI != axisLabelThickness)
           result = axisLabelThickness;
        else if (getAxisLabel() instanceof HasHTML) {
           int charHeight = htmlHeight(
              ((HasHTML) (getAxisLabel())).getHTML());
           result = (int) Math.round((EXTRA_CHARHEIGHT+charHeight) *
                      getTickLabelFontSize() *
                     TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND);
        }
        else
           result = (int) Math.round(
                     (EXTRA_CHARHEIGHT + DEF_CHARHEIGHT) *
                     getTickLabelFontSize() *
                     TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
        return result;
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          for (int j = 0; j < c.getNPoints(); j++) {
             result = maxIgnoreNaNAndMaxValue(result,
                                              c.getPoint(j).getX());
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          for (int j = 0; j < c.getNPoints(); j++) {
             result = minIgnoreNaNAndMaxValue(result,
                                              c.getPoint(j).getX());
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }
    
     public int getTickLabelThickness() { // overrides base class
       int result;
       if (tickLabelThickness != GChart.NAI)
         result = tickLabelThickness;
       else if (getTickCount() == 0)
          result = 0;
       else {
       // XXX: single line labels assumed; these have height
       // almost equal to the fontSize in pixels. Not really
       // right, since multi-line HTML can now be used, but user
       // can explicitly change tick label thickness with
       // multi-line, HTML based, ticks, so OK for now.  
          result = (int) Math.round(
                   TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND *
                   tickLabelFontSize);
       }
       return result;
     }
    
     public void setTickLength(int tickLength) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       GChart.this.chartDecorationsChanged = true;
       plotPanel.plotAreaNeedsUpdate();
       this.tickLength = tickLength;
       tickSymbol.setHeight(tickLength);
     }
     public void setTickThickness(int tickThickness) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       this.tickThickness = tickThickness;
       tickSymbol.setWidth(tickThickness);
     }

     void realizeAxis(PlotPanel pp) {
        // x-axis is drawn via a y gridline
        double x = pp.getXMax();
        if (hasY2Axis()) {
           getY2Axis().gridSymbol.setBackgroundColor(getGridColor());
           getY2Axis().gridSymbol.setHovertextTemplate(null);
           getY2Axis().gridSymbol.realizeSymbol(
              pp,  getY2Axis().gridSymbol.getAnnotation(),
              hasY2Axis(), true, x, pp.getY2Min(),
              Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        }
        else {
           getYAxis().gridSymbol.setBackgroundColor(getGridColor());
           getYAxis().gridSymbol.setHovertextTemplate(null);
           getYAxis().gridSymbol.realizeSymbol(
              pp, getYAxis().gridSymbol.getAnnotation(),
              hasY2Axis(), true, x, pp.getYMin(),
              Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        }
      }
     void realizeTick(PlotPanel pp, int iTick) {
        if (null == getTickWidget(iTick))
           tickSymbol.getAnnotation().setText(getTickText(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
        else
           tickSymbol.getAnnotation().setWidget(getTickWidget(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
          
        tickSymbol.getAnnotation().setFontWeight(
             getTickLabelFontWeight());
        tickSymbol.getAnnotation().setFontColor(
             getTickLabelFontColor());
        tickSymbol.getAnnotation().setFontStyle(
             getTickLabelFontStyle());
        tickSymbol.getAnnotation().setFontSize(
             getTickLabelFontSize());
        double x = getTickPosition(iTick);
        double y = hasY2Axis() ? pp.getY2Min() : pp.getYMin();
        tickSymbol.setBackgroundColor(getGridColor());
        tickSymbol.setHovertextTemplate("");
        tickSymbol.realizeSymbol(pp, tickSymbol.getAnnotation(), hasY2Axis(), true,
                                 x, y,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        // zap no-longer needed refs to help the garbage collector
        tickSymbol.getAnnotation().setText(null);
        tickSymbol.getAnnotation().setWidget(null);
        if (getHasGridlines() && (iTick % ticksPerGridline == 0)) {
          gridSymbol.setBackgroundColor(getGridColor());
          gridSymbol.setHovertextTemplate("");
          gridSymbol.realizeSymbol(pp,  gridSymbol.getAnnotation(),
                  hasY2Axis(), true, x, y,
                  Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        }
     }
  } // end of class XAxis  
  /** The right, or "y2", axis of a GChart.
   *
   * @see GChart#getY2Axis getY2Axis
   */

  public class Y2Axis extends Axis {
     Y2Axis() {
        super();
        setTickSymbol(new Y2TickSymbol());
        setGridSymbol(new Y2GridSymbol());
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y2_AXIS) {
            for (int j = 0; j < c.getNPoints(); j++) {
               result = maxIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y2_AXIS) {
             for (int j = 0; j < c.getNPoints(); j++) {
                result = minIgnoreNaNAndMaxValue(result,
                                                 c.getPoint(j).getY());
            }
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }
     public void setTickLength(int tickLength) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       GChart.this.chartDecorationsChanged = true;
       plotPanel.plotAreaNeedsUpdate();
       this.tickLength = tickLength;
       tickSymbol.setWidth(tickLength);
     }
     public void setTickThickness(int tickThickness) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       this.tickThickness = tickThickness;
       tickSymbol.setHeight(tickThickness);
     }
     void realizeAxis(PlotPanel pp) {
        // y2-axis is drawn via an x gridline
        double x = pp.getXMax();
        double y = pp.getY2Max();
        getXAxis().gridSymbol.setBackgroundColor(getGridColor());
        getXAxis().gridSymbol.setHovertextTemplate("");

        getXAxis().gridSymbol.realizeSymbol(pp, 
           getXAxis().gridSymbol.getAnnotation(), false, true, x, y,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
     }
    
     void realizeTick(PlotPanel pp, int iTick) {
        if (null == getTickWidget(iTick))
           tickSymbol.getAnnotation().setText(getTickText(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
        else
           tickSymbol.getAnnotation().setWidget(getTickWidget(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
          
        tickSymbol.getAnnotation().setFontWeight(
             getTickLabelFontWeight());
        tickSymbol.getAnnotation().setFontColor(
             getTickLabelFontColor());
        tickSymbol.getAnnotation().setFontStyle(
             getTickLabelFontStyle());
        tickSymbol.getAnnotation().setFontSize(
             getTickLabelFontSize());
        double x = pp.getXMax();
        double y2 = getTickPosition(iTick);
        tickSymbol.setBackgroundColor(getGridColor());
        tickSymbol.setHovertextTemplate("");
        tickSymbol.realizeSymbol(pp,  tickSymbol.getAnnotation(),
           true, true, x, y2,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        tickSymbol.getAnnotation().setText(null);
        tickSymbol.getAnnotation().setWidget(null);
        if (getHasGridlines() && (iTick % ticksPerGridline == 0)) {
          gridSymbol.setBackgroundColor(getGridColor());
          gridSymbol.setHovertextTemplate("");
          gridSymbol.realizeSymbol(pp, gridSymbol.getAnnotation(),
           true, true,x, y2,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        }
     }
  } // end of class Y2Axis
  /** The left y-axis of a GChart.
   *
   * @see GChart#getYAxis getYAxis
   *
   */

  public class YAxis extends Axis {
     YAxis() {
        super();
        setTickSymbol(new YTickSymbol());
        setGridSymbol(new YGridSymbol());
     }
     public double getDataMax() {
       double result = -Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y_AXIS) {
            for (int j = 0; j < c.getNPoints(); j++) {
               result = maxIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == -Double.MAX_VALUE ? Double.NaN : result;
     }
     public double getDataMin() {
       double result = Double.MAX_VALUE;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (!c.isVisible()) continue;
          if (c.getYAxis() == Y_AXIS) {
            for (int j = 0; j < c.getNPoints(); j++) {
               result = minIgnoreNaNAndMaxValue(result,
                                                c.getPoint(j).getY());
            }
          }
       }
       return result == Double.MAX_VALUE ? Double.NaN : result;
     }
     public void setTickLength(int tickLength) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       GChart.this.chartDecorationsChanged = true;
       plotPanel.plotAreaNeedsUpdate();
       this.tickLength = tickLength;
       tickSymbol.setWidth(tickLength);
     }
     public void setTickThickness(int tickThickness) {
       plotPanel.invalidateWidgetsAfterCursor(widgetCursor);
       this.tickThickness = tickThickness;
       tickSymbol.setHeight(tickThickness);
     }

     void realizeAxis(PlotPanel pp) {
        // y-axis is drawn via an x gridline
        double x = pp.getXMin();
        double y = pp.getYMax();
        getXAxis().gridSymbol.setBackgroundColor(getGridColor());
        getXAxis().gridSymbol.setHovertextTemplate("");
        getXAxis().gridSymbol.realizeSymbol(pp,
           getXAxis().gridSymbol.getAnnotation(), false, true,
           x, y,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
     }
    
     void realizeTick(PlotPanel pp, int iTick) {
        if (null == getTickWidget(iTick))
           tickSymbol.getAnnotation().setText(getTickText(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
        else
           tickSymbol.getAnnotation().setWidget(getTickWidget(iTick),
                                              getTickWidthUpperBound(iTick),
                                              getTickHeightUpperBound(iTick));
          
        tickSymbol.getAnnotation().setFontWeight(
             getTickLabelFontWeight());
        tickSymbol.getAnnotation().setFontColor(
             getTickLabelFontColor());
        tickSymbol.getAnnotation().setFontStyle(
             getTickLabelFontStyle());
        tickSymbol.getAnnotation().setFontSize(
             getTickLabelFontSize());
        double x = pp.getXMin();
        double y = getTickPosition(iTick);
       
        tickSymbol.setBackgroundColor(getGridColor());
        tickSymbol.setHovertextTemplate("");
        tickSymbol.realizeSymbol(pp,  tickSymbol.getAnnotation(),
           false, true, x, y,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        tickSymbol.getAnnotation().setText(null);
        if (getHasGridlines() && (iTick % ticksPerGridline == 0)) {
          gridSymbol.setBackgroundColor(getGridColor());
          gridSymbol.setHovertextTemplate("");
          gridSymbol.realizeSymbol(pp,  gridSymbol.getAnnotation(),
           false, true, x, y,
           Double.NaN, Double.NaN, Double.NaN, Double.NaN);
        }
    }
    
  } // end of class YAxis

  // Allows precise alignment of a text label before the
  // exact size of that label is known, by enclosing it
  // within a 1x1 grid. Tried VerticalPanel and HorizontalPanel
  // but they have a less complete Java API and were
  // (surprisingly) no more efficient than a 1,1 Grid
  private static class AlignedLabel extends Grid {
    AlignedLabel() {
      super(1,1);
      getCellFormatter().setWordWrap(0,0,false);
      setCellPadding(0);
      setCellSpacing(0);
      setBorderWidth(0);
    }   
  }
   private class GridSymbol extends Symbol {
    GridSymbol() {
      super(null);
      setBackgroundColor(DEFAULT_GRID_COLOR);
      setBorderColor(DEFAULT_GRID_COLOR);
      setBorderStyle(GRID_BORDER_STYLE);
      setBorderWidth(GRID_BORDER_WIDTH);
      setWidth(DEFAULT_GRID_WIDTH);
      setHeight(DEFAULT_GRID_HEIGHT);
    }
  }

  /*
   * A cursor into the reusable lists of images and grids that
   * a ReusuableAbsolutePanel maintains. Lets us keep track of
   * where in the list the widgets from each "chunk" of the
   * chart (each curve represents such a chunk) begin, so
   * that we can avoid regenerating them if they, and
   * the chunks preceeding them, have not changed.
   *
   */
  static class WidgetCursor {
       int imageIndex = 0;
       int labelIndex = 0;
       WidgetCursor() {}
       WidgetCursor(int imageIndex, int labelIndex) {
         this.imageIndex = imageIndex;
         this.labelIndex = labelIndex;
       }
       // Greater than or equal to. Relies on fact that
       // image and label indexes increase (or at least stay
       // the same) as chart rendering progresses.
       boolean greaterThanOrEqualTo(WidgetCursor widgetCursor) {
          if (imageIndex >= widgetCursor.imageIndex &&
              labelIndex >= widgetCursor.labelIndex)
           return true;
         else
           return false;
       }
      
       void copy(WidgetCursor widgetCursor) {
         this.imageIndex = widgetCursor.imageIndex;
         this.labelIndex = widgetCursor.labelIndex;
       }
      
    } // end of class WidgetCursor

  /*
   * This class' sole purpose is to work around a FF 2
   * performance limitation: chart update times increase as
   * O(N^2) after the number of direct ancestor child widgets in
   * a single AbsolutePanel exceeds around 500-1000, AND the
   * chart is updated in more than one browser-displayed stage
   * (e.g, via a series of incremental updates that successively
   * add more curve data, for purposes of user feedback). By
   * contrast, IE7 times grow as O(N) even if 3,000 child widgets
   * are added to a previously displayed chart (e.g. by adding a
   * bar curve with 3,000 bars on it).
   *
   * <p>
   *
   * For solid-fill line chart support (LINE SymbolType
   * introduced in 2.2), thousands of widgets are often needed
   * and so these O(N^2) FF 2 times were just too slow.
   *
   * <p>
   *
   * Some kind of fixed hash table inside of FF 2 divs could
   * explain this switch from O(N) to O(N^2) performance.
   * <p>
   *
   * Approach is to split up the large AbsolutePanel into a
   * series of child panels, each of which contains a number of
   * elements within the range where FF 2 updates are O(N).
   * <p>
   *
   * Tried to keep it light-weight so IE7 isn't penalized too
   * much for having to workaround this FF 2 limitation.
   *
   */

  static class PartitionedAbsolutePanel extends Composite {
/* Max number of widgets in each panel; chose a value as large as
   possible while remaining within the empirically
   determined FF 2 O(N) range. Here's the data (from a 3,000 element
   test based on the sin curve of the 2.1 live demo
   called GChartExample15c.java) upon which this choice was based:
<p>

<pre>

Size    FF2    IE7
        (sec)  (sec)
  1     ~14     16
  2      11     13
32      9      11  
64      9      12
128      9      11
256      9      11
512      10     11
1024     15     11
2048     27     11
4096*    61     12

</pre>
<p>

The two largest sizes are good approximations of FF2 times we got
informally before the switch to partioned AbsolutePanels with
charts with the corresponding number of elements (2000 or 3000).
The overhead of the partioning itself is very low, as shown by
the modest time increase even when each element is placed into
its own sub-panel. Tests with a < 256 element chart suggested at
most a couple of ms of time increases due to the introduction of
partioning.

<p>

I kind of expected to see >61 second times with a sub-panel size
of 1, since the parent panel still has >3,000 elements in this
case. Whatever the cause of the performance logjam (presumably in
the FF2 heap somewhere?)  simply the fact that you introduce a
parent AbsolutePanel that holds various child AbslolutePanels
that hold the actual image widgets appears to work around most of
the problem. If such a useless change makes FF2 materially
faster, that seems like a performance bug in FF2 to me.<p>

The root FF cause is apparently NOT image cache related, though,
since turning off the image cache via about:config didn't change
times for the last row of the table above.

*/
    
    final int WIDGETS_PER_PANEL = 256;
    private AbsolutePanel root = new AbsolutePanel();
    private AbsolutePanel subPanel = null; // "selected" subPanel
    private int iSubPanel = -1;    // index of "selected" subPanel
    private int nWidgets = 0;     // total # over all subPanels
  
    PartitionedAbsolutePanel() {
       initWidget(root);
    }
   
    public int getWidgetCount() {
       return nWidgets;
    }

    // makes the subpanel containing the widget the selected one.
    private void selectSubPanel(int iWidget) {
       if (iSubPanel != iWidget/WIDGETS_PER_PANEL) {
          iSubPanel = iWidget/WIDGETS_PER_PANEL;
          subPanel = (AbsolutePanel) root.getWidget(iSubPanel);
       }
    }
      
    // adds a widget to end of this partioned absolute panel
    public void add(Widget w) {
       if (nWidgets % WIDGETS_PER_PANEL == 0) {
          // last panel is full, time to add a new one
          subPanel = new AbsolutePanel();
          // Panel sits in upper left corner. Does nothing, can't
          // be seen. It's just a holder for other widgets.
          GChart.setOverflow(subPanel, "visible");
          subPanel.setPixelSize(0,0);
          root.add(subPanel, 0, 0);
       }
       selectSubPanel(nWidgets);
       subPanel.add(w);
       nWidgets++;
    }

    // returns widget at given index
    public Widget getWidget(int iWidget) {
       if (iWidget < 0 || iWidget >= nWidgets)
             throw new IllegalArgumentException(
"Invalid widget index: " + iWidget +
". Valid range is: 0..." + (nWidgets-1));

       selectSubPanel(iWidget);
       Widget result = subPanel.getWidget(
                          iWidget % WIDGETS_PER_PANEL);
       return result;
    }
 
    // Remove very last widget from panel.
    public boolean remove(int iWidget) {
       if (iWidget != nWidgets-1)
          throw new IllegalArgumentException(
"iWidgets arg = " + iWidget + " nWidgets-1 (" + (nWidgets-1)+") is required.");

       selectSubPanel(iWidget);
       boolean result = subPanel.remove(iWidget % WIDGETS_PER_PANEL);
       if (iWidget % WIDGETS_PER_PANEL == 0) {
       // if deleted widget is last widget overall, and first on
       // the selected panel, selected panel will now be empty.
         root.remove(subPanel);
         iSubPanel = -1;    // next selectSubPanel will reset these
         subPanel = null;
       }
       nWidgets--;
       return result;
    }

   
   
     // To assure that w is on the selected subPanel, this method
     // must only be passed a widget that is in the currently
     // selected subPanel. This can be assured by passing in a
     // widget that was just added via add(), or just retrieved
     // via getWidget (otherwise, an exception will be thrown).
     
    public void setWidgetPosition(Widget w, int left, int top) {
       subPanel.setWidgetPosition(w, left, top);
    }
   
  } // end of class PartitionedAbsolutePanel
 
   /*
    * AbsolutePanel that allows images and labels it contains to
    * be easily reused, for increased efficiency.
    * <p>
    *
    * Rather than clearing the widgets contained in the panel and
    * recreating and adding them back as needed (which is
    * expensive) the widgets are made invisible when not in use
    * and made visible again when they are needed (which is usually
    * at least twice as fast).
    *
    * <p> In principle, this is less memory efficient, but in
    * practice, due to the fact that there is less likelyhood of
    * fragmentation with reuse than with relying of the garbage
    * collector, it could even be more memory efficient.
    *
    */
  class ReusableAbsolutePanel extends AbsolutePanel {
     private AbsolutePanel backgroundPanel = new AbsolutePanel();
     private PartitionedAbsolutePanel imagePanel = new PartitionedAbsolutePanel();
     private PartitionedAbsolutePanel labelPanel = new PartitionedAbsolutePanel();
     // helps minimize calls to setVisible (which can be expensive)
     private int lastVisibleImage = -1;
     private int lastVisibleLabel = -1;

/**
  * Provides support for reusing certain property specifications
  * that are likely to be the same, given how images in a GChart
  * get reused, and given certain assumptions about which
  * properties of the image are most likely to remain unchanged
  * between updates.  For the most common scenarios, chart
  * updates are significantly faster due to replacing (relatively
  * expensive) DOM style attribute setting with (relatively
  * cheap) String reference or integer equality tests.
  * <p>
  *
  * For hovertext, the class also lets us defer actual generation
  * of the hovertext until they actually mouse over the image,
  * saving further time (it's surprisingly expensive just to
  * format the numbers and such used in hovertexts).
  *
  */
  class ReusableImage extends Image {     
     private String backgroundColor = USE_CSS;
     private String borderColor = USE_CSS;
     private String borderStyle= USE_CSS;
     private int cappedBorderWidth = GChart.NAI;
     private int width = GChart.NAI;
     private int height = GChart.NAI;
     private HovertextChunk[] hovertextChunks = null;
     private double hoverX;
     private double hoverY;
     private boolean hoverOnY2;
     private double hoverPieSliceSize;
     int x = GChart.NAI;
     int y = GChart.NAI;
     String url = null;
    
     ReusableImage() {
        super();
        this.url = null
        sinkEvents(Event.ONMOUSEOVER);
     }

     // avoid mouselistener, simpler and hopefully more efficient.
     public void onBrowserEvent(Event evt) {
        if (DOM.eventGetType(evt) == Event.ONMOUSEOVER) {
          String hoverText = "";
          if (hovertextChunks != null) {
             hoverText = GChart.getHovertext(hovertextChunks,
                           hoverX, hoverY, hoverOnY2,
                           hoverPieSliceSize,
                           getXAxis(), getYAxis(), getY2Axis());
          }
          setTitle(hoverText);
        }
     }

     // Specifies info required to generate a hovertext.  Why not
     // just set the hovertext? Because this approach allows us
     // to defer the work of generating hovertext until somebody
     // actually hovers (it's faster).
     void setHovertextInfo(HovertextChunk[] htc,
                           double x, double y,
                           boolean onY2,
                           double pieSliceSize) {
        hovertextChunks = htc;
        hoverX = x;
        hoverY = y;
        hoverOnY2 = onY2;
        hoverPieSliceSize = pieSliceSize;
     }
    
          
     void setReusableProperties(ReusableAbsolutePanel ap,
                                       String backgroundColor,
                                       String borderColor,
                                       String borderStyle,
                                       int borderWidth,
                                       double dWidth,
                                       double dHeight,
                                       double x,
                                       double y,
                                       String url) {

      // Round two edges, and define width to be their difference. 
      // (rounding this way assures bars align with gridlines, etc.)
      int newX = (int) Math.round(x);
      int newW = (int) Math.round(x + dWidth) - newX;
      int newY =  (int) Math.round(y);
      int newH = (int) Math.round(y + dHeight) - newY;
      // Don't allow borders that would exceed specified width or
      // height. So, if smaller of width, height is at least twice the
      // border width, border width is used as is, otherwise,
      // it's replaced with half the smaller of width, height:
      int newCappedBorderWidth =
         ((2*borderWidth <= ((newW < newH) ? newW : newH)) ?
          borderWidth : (((newW < newH) ? newW : newH)/2));                       
      if (cappedBorderWidth != newCappedBorderWidth) {
         DOM.setStyleAttribute(getElement(),
             "borderWidth", newCappedBorderWidth+"px");
         cappedBorderWidth = newCappedBorderWidth;
      }
// Note: on a GWT absolute panel, the x,y position of the
// widget is the upper left corner of the widget's border,    
// so x, y need no adjustment to account for the border
      newH -= 2*cappedBorderWidth;
      newW -= 2*cappedBorderWidth;

      if (GChart.NAI == this.x) {
         // At first, use AbsolutePanel's official API
         // (to insulate us from any future AbsolutePanel changes)
         ap.setImagePosition(this, newX, newY);
         this.x = newX;
         this.y = newY;
      }
      else { // for speed, just set the edge positions that changed
             // (works, but bypasses AbsolutePanel's official API)
         if (this.x != newX) {
            DOM.setStyleAttribute(getElement(),"left", newX+"px");
            this.x = newX;
         }
         if (this.y != newY) {
            DOM.setStyleAttribute(getElement(),"top", newY+"px");
            this.y = newY;
         }
      }

      if (this.width != newW) {
        setWidth(newW + "px");
        this.width = newW;
      }
      if (this.height != newH) {
        setHeight(newH + "px");
        this.height = newH;
      }
 
   // border was too big to fit inside rectangle; since GChart
   // borders are uniform around the rectangle, odd-sized
   // dimensions can leave a single "leftover" 1px inside the
   // border. Set background to the border's color so that the
   // border, in effect, takes up the entire rectangle.
      if (cappedBorderWidth < borderWidth)
         backgroundColor = borderColor;
      if (this.backgroundColor != backgroundColor) {
         DOM.setStyleAttribute(getElement(), "backgroundColor",
                               backgroundColor);
         this.backgroundColor =backgroundColor;
      }
      if (this.borderColor != borderColor) {
         DOM.setStyleAttribute(getElement(), "borderColor",
                               borderColor);
         this.borderColor =borderColor;
      }
      if (this.borderStyle != borderStyle) {
         DOM.setStyleAttribute(getElement(), "borderStyle",
                               borderStyle);
         this.borderStyle = borderStyle;
      }

      if (this.url != url) {
/*
* WARNING: Redundant setUrls cause leaks in FF 2.0.0.16. So, be
* particularly careful not to accidentally "double set" a URL to
* the exact same URL (I did this with a slightly less efficient
* initialization of my images, and this caused a huge memory
* leak that I hope to memorialize, and lay to rest forever, here.)
*
* Symptoms, in FF 2 only, are those that would occur AS IF the
* extra setUrl increased the reference count on the (browser
* cached) image file so that Firefox can't release either it, or
* any of the img elements that reference it. A very big leak for
* GChart, since just about everything in a GChart references the
* exact same blank gif URL.
*
* Such symptoms did not occur in IE7, or in FF 2 if the cache
* has been disabled via "about:config".
*
* Search for "massively leak" and below that comment you will
* find two lines that, if uncommented, make the leak reappear.
*/
         setUrl(url);
         this.url = url;
      }     
    }
  } // end of class ReusableImage

/**
  * Provides support for reusing certain property specifications
  * that are likely to be the same, given how aligned labels in a
  * GChart get reused, and given certain assumptions about which
  * properties of the labels are most likely to remain unchanged
  * between updates.
  *
  * Very similar in intent to the ReusableImage, see that
  * class' header comment for more info.
  *
  */
  class ReusableAlignedLabel extends AlignedLabel {
     int fontSize = GChart.NAI;
     String fontStyle = USE_CSS;
     String fontWeight = USE_CSS;
     String fontColor = USE_CSS;
     HasHorizontalAlignment.HorizontalAlignmentConstant hAlign;
     HasVerticalAlignment.VerticalAlignmentConstant vAlign;
     private HovertextChunk[] hovertextChunks = null;
     private double hoverX;
     private double hoverY;
     private boolean hoverOnY2;
     private double hoverPieSliceSize;
     String labelText = null;
     boolean isHTML = false;
     Widget labelWidget = null;
     final AlignedLabel innerGrid = new AlignedLabel();
    
     ReusableAlignedLabel() {
        super();
        sinkEvents(Event.ONMOUSEOVER);
        setWidget(0, 0, innerGrid);
      /*
       * The basic technique being used in the lines below is
       * illustrated by this excerpt from p 317 of "CSS, The
       * Definitive Guide" by Eric A. Meyer: <p>
       *
       * <pre>
       *   p.clear {visibility: hidden;}
       *   p.clear em {visibility: visible;}
       * </pre>
       * <p>
       *
       * In the above example, emphasized (italic) text is
       * positioned exactly as it would have been in the
       * paragraph had the normal text been visible, except that
       * the normal text <i>isn't</i> visible. And, unlike
       * transparent text, the invisible text also won't capture
       * mouse events (essential for our aligned labels)
       *
       * <p>
       *
       * With GChart's aligned label, we want the outter Grid (HTML
       * table) to be "not there" as far as visibility and
       * mouseovers, but still impact centering and
       * other alignment of the stuff in the visible, inner Grid.
       *
       * <p>
       *
       * Note that we cannot just make the Grid color transparent
       * (tried that first) because in that case the oversized
       * outter Grid required for alignment will still grab the
       * mouseover events inappropriately (wrong hovertext
       * problem).
       *
       * <p>
       *
       * Not certain but, apparently, IE6 requires that, if
       * you want to apply this trick when the outter element
       * is a table you must use another table as the
       * inner element. At least, the div inner element approach
       * I used at first (that basically worked in Firefox), made
       * both parent and child invisible in IE6.
       * <p>
       *
       * In summary:
       *
       * The upside: alignment without inappropriate event occlusion
       * The downside: the extra Grid element saps performance
       *
       */
       
        DOM.setStyleAttribute(getElement(),
                                 "visibility","hidden");
        DOM.setStyleAttribute(innerGrid.getElement(),
                                 "visibility", "visible");
     }

     public void onBrowserEvent(Event evt) {
        if (DOM.eventGetType(evt) == Event.ONMOUSEOVER) {
          String hoverText = "";
          if (hovertextChunks != null) {
             hoverText = GChart.getHovertext(hovertextChunks,
                                       hoverX, hoverY, hoverOnY2,
                                       hoverPieSliceSize,
                                       getXAxis(), getYAxis(),
                                       getY2Axis());
          }
          innerGrid.setTitle(hoverText);
        }
     }
    
     // Specifies info required to generate a hovertext.  Why not
     // just set the hovertext? Because this approach allows us
     // to defer the work of generating hovertext until somebody
     // actually hovers (it's faster).
     void setHovertextInfo(HovertextChunk[] htc,
                           double x, double y,
                           boolean onY2,
                           double pieSliceSize) {
        hovertextChunks = htc;
        hoverX = x;
        hoverY = y;
        hoverOnY2 = onY2;
        hoverPieSliceSize = pieSliceSize;
     }

     // Sets properties only if they have changed; to replace
     // expensive DOM calls with cheap inequality tests.
    
     void setReusableProperties(
        int fontSize,
        String fontStyle,
        String fontWeight,
        String fontColor,
        HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
        HasVerticalAlignment.VerticalAlignmentConstant vAlign,
        String labelText,
        boolean isHTML,
        Widget labelWidget) {

      if (this.fontSize != fontSize) {
         DOM.setIntStyleAttribute(innerGrid.getElement(), "fontSize", fontSize);
         this.fontSize = fontSize;
      }
      if (this.fontStyle != fontStyle) {
         DOM.setStyleAttribute(innerGrid.getElement(), "fontStyle", fontStyle);
         this.fontStyle = fontStyle;
      }
      if (this.fontWeight != fontWeight) {
         DOM.setStyleAttribute(innerGrid.getElement(), "fontWeight", fontWeight);
         this.fontWeight = fontWeight;
      }
      if (this.fontColor != fontColor) {
         DOM.setStyleAttribute(innerGrid.getElement(),"color", fontColor);
         this.fontColor = fontColor;
      }
      if (this.hAlign != hAlign) {
        getCellFormatter().setHorizontalAlignment(0,0,hAlign);
// without this, only IE6-quirks doesn't quite align right:
        innerGrid.getCellFormatter().setHorizontalAlignment(0,0,hAlign);
        this.hAlign = hAlign;
      }
      if (this.vAlign != vAlign) {
        getCellFormatter().setVerticalAlignment(0,0,vAlign);
// without this, only IE6-quirks doesn't quite align right:
        innerGrid.getCellFormatter().setVerticalAlignment(0,0,vAlign);
        this.vAlign = vAlign;
      }

      if (null != labelWidget) {
         if (this.labelWidget != labelWidget) {
            innerGrid.setWidget(0,0,labelWidget);
            this.labelWidget = labelWidget;
            this.labelText = null;
         }
      }
      else if (this.labelText != labelText || this.isHTML != isHTML) {
         if (null == labelText || "" == labelText)
            innerGrid.setText(0,0,"");
         else if (!isHTML) {
            innerGrid.setText(0,0,labelText);
         }
         else {
            innerGrid.setHTML(0, 0, labelText);
         }
         this.isHTML = isHTML;
         this.labelText = labelText;
         this.labelWidget = null;
      }
    }
  } // end of class ReusableAlignedLabel

  ReusableAbsolutePanel() {
        // allows labels, symbols, that extend a tad off the
        // chart proper to still appear on the chart; AbsolutePanel
        // default is to truncate these, which looks bad.
       GChart.setOverflow(this, "visible");
       GChart.setOverflow(backgroundPanel, "visible");
       GChart.setOverflow(imagePanel, "visible");
       GChart.setOverflow(labelPanel, "visible");
       // these sub-panels have no size themselves, they are merely
       // there to segregate background, images, and labels.
       backgroundPanel.setPixelSize(0,0);
       imagePanel.setPixelSize(0,0);
       labelPanel.setPixelSize(0,0);
       // always added first so it's always "in the background"
       this.add(backgroundPanel,0,0);
       this.add(imagePanel, 0, 0);
       this.add(labelPanel, 0, 0);
     }
     WidgetCursor INITIAL_WIDGET_CURSOR_POSITION = new WidgetCursor();
    
     private WidgetCursor currentWidgetCursor = new WidgetCursor();
     private WidgetCursor firstInvalidWidgetCursor =
       new WidgetCursor();

     WidgetCursor getWidgetCursor() {
       return currentWidgetCursor;
     }
     WidgetCursor getFirstInvalidWidgetCursor() {
       return firstInvalidWidgetCursor;
     }

     // returns an AbsolutePanel whose widgets are always behind
     // all the reusuable Images and AlignedLabels
     AbsolutePanel getBackgroundPanel() {
        return backgroundPanel; 
     }
     /* Declares that any widgets previously created that come
      * after the given cursor position are no
      * longer consistent with the chart's specificaiton and thus
      * may be reused the next time that the chart is updated.
      */
     void invalidateWidgetsAfterCursor(WidgetCursor widgetCursor) {
       if (null != widgetCursor &&
         firstInvalidWidgetCursor.greaterThanOrEqualTo(widgetCursor))
         firstInvalidWidgetCursor.copy(widgetCursor);
     }

     void setImagePosition(Image img, int x, int y) {
        imagePanel.setWidgetPosition(img, x, y);
     }
     void setLabelPosition(AlignedLabel lbl, int x, int y) {
        labelPanel.setWidgetPosition(lbl, x, y);
     }

     // Tells panel you are ready to start allocating widgets
     void beginReusingWidgets() {
       currentWidgetCursor.copy(firstInvalidWidgetCursor);
     }
     // Tells panel you are done allocating widgets, and
     // it's OK to do any final cleanup/bookkeeping needed.
     void endReusingWidgets() {
       // hide or remove images no longer being used
        for (int iImage = optimizeForMemory ?
                          (imagePanel.getWidgetCount()-1) :
                          lastVisibleImage;
            iImage >= currentWidgetCursor.imageIndex;
            iImage--) {
          Widget w = imagePanel.getWidget(iImage);
          if (optimizeForMemory)
             imagePanel.remove(iImage);
          else
             w.setVisible(false);
       }
       lastVisibleImage = currentWidgetCursor.imageIndex - 1;
           
       // hide or remove labels no longer being used
       for (int iLabel = optimizeForMemory ?
                         (labelPanel.getWidgetCount()-1) :
                          lastVisibleLabel;
            iLabel >= currentWidgetCursor.labelIndex;
            iLabel--) {
          Widget w = labelPanel.getWidget(iLabel);
          if (optimizeForMemory)
             labelPanel.remove(iLabel);
          else
             w.setVisible(false);
       }
       lastVisibleLabel = currentWidgetCursor.labelIndex-1;
           
       firstInvalidWidgetCursor.copy(currentWidgetCursor);      
     }

     /* Speedier, reusable, plot-panel-managed images. In effect,
        turns plot panel into a specialized memory manager. */
     ReusableImage getNextOrNewImage(
                                  String backgroundColor,
                                  String borderColor,
                                  String borderStyle,
                                  int borderWidth,
                                  double width,
                                  double height,
                                  double x, double y,
                                  String url) {
       ReusableImage result;
       if (currentWidgetCursor.imageIndex <
           imagePanel.getWidgetCount()) { // reuse an old image      
         result = (ReusableImage) imagePanel.getWidget(
           currentWidgetCursor.imageIndex);
         if (currentWidgetCursor.imageIndex > lastVisibleImage)
           result.setVisible(true);
       }
       else {                             // add a new image 
         result = new ReusableImage();
         imagePanel.add(result);
       }

       result.setReusableProperties(this, backgroundColor,
                                    borderColor,
                                    borderStyle,
                                    borderWidth,
                                    width,
                                    height,
                                    x, y, url);
                                  
       if (lastVisibleImage < currentWidgetCursor.imageIndex)
         lastVisibleImage = currentWidgetCursor.imageIndex;
       currentWidgetCursor.imageIndex++;
       return result;
     }

     /* Similar to getNextOrNewImage, but for aligned labels */
     ReusableAlignedLabel getNextOrNewAlignedLabel(
        int fontSize,
        String fontStyle,
        String fontWeight,
        String fontColor,
        HasHorizontalAlignment.HorizontalAlignmentConstant hAlign,
        HasVerticalAlignment.VerticalAlignmentConstant vAlign,
        HovertextChunk[] htc,
        double hoverX, double hoverY, boolean onY2,
        double hoverPieSliceSize,
        String labelText,
        boolean isHTML,
        Widget labelWidget) {
       ReusableAlignedLabel result;
       if (currentWidgetCursor.labelIndex <
           labelPanel.getWidgetCount()) {      
         result = (ReusableAlignedLabel) labelPanel.getWidget(
             currentWidgetCursor.labelIndex);
         if (currentWidgetCursor.labelIndex > lastVisibleLabel)
           result.setVisible(true);
       }
       else {
         result = new ReusableAlignedLabel();
         labelPanel.add(result);
       }
       result.setReusableProperties(fontSize, fontStyle, fontWeight,
        fontColor, hAlign, vAlign, labelText, isHTML, labelWidget);
       result.setHovertextInfo(htc, hoverX, hoverY, onY2,
                               hoverPieSliceSize);
                                   
       if (lastVisibleLabel < currentWidgetCursor.labelIndex)
         lastVisibleLabel = currentWidgetCursor.labelIndex;
       currentWidgetCursor.labelIndex++;
       return result;
     }
    
   } // end of class ReusableAbsolutePanel
  
   class PlotPanel extends ReusableAbsolutePanel {

     private int topMargin;
     private int xAxisEnsembleHeight;
     private int xChartSize;
     private double xMax;
     private double xMin;
     private int y2AxisEnsembleWidth;
     private double y2Max;
     private double y2Min;
     private int yAxisEnsembleWidth;
     private int yChartSize;
     private double yMax;
     private double yMin;
     boolean plotAreaNeedsUpdate = true;
// Need to be very careful to NEVER redundantly set any Image URL
// to avoid trigging a leak in FF 2.0.0.16  (see below):
     private String newPlotAreaImageURL = null;    
     private String plotAreaImageURL = null;
    
     PlotPanel() {
        super();
// Initial image must be undefined to assure no Firefox 2 leaks,
// and to assure that actual URL at time of update is used.
// c.f. setUrl "WARNING" in ReusableImage.setReusableProperties
        getBackgroundPanel().add(new Image());
     }

     // indicates that the plot area background image has changed
     void plotAreaNeedsUpdate() { plotAreaNeedsUpdate = true; }
    
     Image getPlotAreaImage() {
        Image result = (Image) getBackgroundPanel().getWidget(0);
        return result;
     }

     void updatePlotAreaImageURLOnlyIfNeeded() {
// Must guard against any possible redundant setURL to avoid a
// leak in FF 2.0.0.16, c.f. setUrl related "WARNING:" in
// ReusableImage.setReusableProperties.
        if (getPlotAreaImageURL() != plotAreaImageURL) {
           getPlotAreaImage().setUrl(getPlotAreaImageURL());
           plotAreaImageURL = getPlotAreaImageURL();
         }
     }

     void setPlotAreaImageURL(String imageURL) {
        newPlotAreaImageURL = imageURL;
     }

     String getPlotAreaImageURL() {
        String result = (null == newPlotAreaImageURL) ?
                        getBlankImageURL():
                        newPlotAreaImageURL;
        return result;
     }
    
     int getXAxisEnsembleHeight() {
        return xAxisEnsembleHeight;
     }
     double getXMax() {
        return xMax;
     }
     double getXMin() {
        return xMin;
     }
     int getY2AxisEnsembleWidth() {
        return y2AxisEnsembleWidth;
     }
     double getY2Max() {
        return y2Max;
     }
     double getY2Min() {
        return y2Min;
     }
     int getYAxisEnsembleWidth() {
        return yAxisEnsembleWidth;
     }
    
     double getYMax() {
        return yMax;
     }
     double getYMin() {
        return yMin;
     }

     void reset(int xChartSize, int yChartSize,
                       boolean hasYAxis, boolean hasY2Axis,
                       Axis xAxis, Axis yAxis, Axis y2Axis) {

        this.xChartSize = xChartSize;
        this.yChartSize = yChartSize;

        Axis.AxisLimits axisLimits = xAxis.getAxisLimits();
        xMin = axisLimits.min;
        xMax = axisLimits.max;
        axisLimits = yAxis.getAxisLimits();
        yMin = axisLimits.min;
        yMax = axisLimits.max;
        axisLimits = y2Axis.getAxisLimits();
        y2Min = axisLimits.min;
        y2Max = axisLimits.max;

        topMargin = getChartTitleThickness();
       
        xAxisEnsembleHeight = xAxis.getAxisLabelThickness() +
            xAxis.getTickLabelThickness() + xAxis.getTickLength();
        if (!hasYAxis) {
           yAxisEnsembleWidth = yAxis.getTickLabelThickness() +
                                yAxis.getTickLength();
        }
        else {
          yAxisEnsembleWidth = yAxis.getAxisLabelThickness() +
                               yAxis.getTickLabelThickness() +
                               yAxis.getTickLength();
        }
        if (!hasY2Axis)
           y2AxisEnsembleWidth = y2Axis.getTickLabelThickness() +
                                 y2Axis.getTickLength();      
        else
           y2AxisEnsembleWidth = y2Axis.getAxisLabelThickness() +
                                 y2Axis.getTickLabelThickness() +
                                 y2Axis.getTickLength();      


        setPixelSize(getXChartSizeDecorated(),
                     getYChartSizeDecorated());

// If you uncomment the next two lines, the GChart test application
// will massively leak in FF 2.0.0.16. Search for "WARNING:"
// comment for more details.
//
//      getPlotAreaImage().setUrl(getPlotAreaImageURL());
//      getPlotAreaImage().setUrl(getPlotAreaImageURL());
       
// define plot background and such, via the
// ReusableAbsolutePanel's background widget
        if (plotAreaNeedsUpdate) {

          int cappedBorderWidth =
            (int) Math.min(getPlotAreaBorderWidth(),
                         Math.min(xChartSize/2,yChartSize/2));

          updatePlotAreaImageURLOnlyIfNeeded();

          setBackgroundColor(getPlotAreaImage(),
                           getPlotAreaBackgroundColor());
          setBorderColor(getPlotAreaImage(),
                       getPlotAreaBorderColor());
          setBorderStyle(getPlotAreaImage(),
                       getPlotAreaBorderStyle());

          setBorderWidth(getPlotAreaImage(), cappedBorderWidth);
          getPlotAreaImage().setPixelSize(
            xChartSize-2*cappedBorderWidth,
            yChartSize-2*cappedBorderWidth);
          getBackgroundPanel().setWidgetPosition(getPlotAreaImage(),
             yAxisEnsembleWidth, topMargin);
          plotAreaNeedsUpdate = false;
        }
     }

     boolean pointInPlotArea(int xPx, int yPx) {
       boolean result = false;
       // 1 px wider: avoids in-range point loss due to rounding
       int d = 1;
       if (withinRange(xPx, yAxisEnsembleWidth-d,
                            yAxisEnsembleWidth+xChartSize-1+d) &&
           withinRange(yPx, topMargin-d, topMargin+yChartSize-1+d))
         result = true;
       return result;
     }
    
     double xToPixel(double x) {
        double result = Double.NaN;
        if (-Double.MAX_VALUE == x)
           result = yAxisEnsembleWidth;
        else if (Double.MAX_VALUE == x)
           result = yAxisEnsembleWidth+xChartSize-1.0;
        else if (!(x!=x)) { // x!=x is a faster isNaN
          result =            
          (yAxisEnsembleWidth * (xMax - x) +
          (yAxisEnsembleWidth+xChartSize-1.0) * (x - xMin))/
                  (xMax - xMin);
        }
       
        return result;
     }

     double xPixelToX(double xPx) {
        double result = Double.NaN;
        if (!(xPx!=xPx)) { // x!=x is a faster isNaN
           result = xMin + (xMax - xMin) *
                    (xPx - yAxisEnsembleWidth)/(xChartSize-1.);
        }
        return result;
     }
    
     double dxToPixel(double dx) {
// xMax and xMin are at centers of their pixels, hence the -1
        double result = (dx * (xChartSize-1))/(xMax-xMin);
        return result;
     }
    
     double yToPixel(double y, boolean isY2) {
        double minY = isY2 ? y2Min : yMin;
        double maxY = isY2 ? y2Max : yMax;
        double result = Double.NaN;
        if (-Double.MAX_VALUE == y)
           result = yChartSize + topMargin - 1.0;
        else if (Double.MAX_VALUE == y)
           result = topMargin;
        else if (!(y!=y)) // x!=x is a faster isNaN
           result = (topMargin * (y - minY) +
                    ((yChartSize + topMargin - 1.0) *
                     (maxY - y)))/(maxY - minY);
        return result;
     }

     double yPixelToY(double yPx, boolean isY2) {
        double result = Double.NaN;
        if (!(yPx!=yPx)) // x!=x is a faster isNaN
           result = yMax + (yMin - yMax) *
                    (yPx - topMargin)/(yChartSize-1.);
        return result;
     }
    
     double dyToPixel(double dy, boolean isY2) {
        double minY = isY2 ? y2Min : yMin;
        double maxY = isY2 ? y2Max : yMax;
// maxY and minY are at centers of their pixels, hence the -1
        double result = (dy * (yChartSize-1))/(maxY-minY);
        return result;
     }

   } // end of class PlotPanel
  
   private class TickSymbol extends Symbol {
    TickSymbol() {
      super(null); // a curve-disembodied symbol with no curve parent
      setBackgroundColor(DEFAULT_GRID_COLOR);
      setBorderColor(DEFAULT_GRID_COLOR);
      setBorderStyle(TICK_BORDER_STYLE);
      setBorderWidth(TICK_BORDER_WIDTH);
    }
  }

   private class XGridSymbol extends GridSymbol {
     XGridSymbol() {
        super();
        setSymbolType(SymbolType.XGRIDLINE);
     }
  }
  
   private class XTickSymbol extends TickSymbol {
     XTickSymbol() {
        super();
        setSymbolType(SymbolType.BOX_SOUTH);
        setWidth(DEFAULT_TICK_THICKNESS);
        setHeight(DEFAULT_TICK_LENGTH);
        getAnnotation().setLocation(AnnotationLocation.SOUTH);
     }
  }

   private class Y2GridSymbol extends GridSymbol {
     Y2GridSymbol() {
        super();
        setSymbolType(SymbolType.Y2GRIDLINE);
     }
  }
  
   private class Y2TickSymbol extends TickSymbol {
     Y2TickSymbol() {
        super();
        setSymbolType(SymbolType.BOX_EAST);
        setWidth(DEFAULT_TICK_LENGTH);
        setHeight(DEFAULT_TICK_THICKNESS);
        getAnnotation().setLocation(AnnotationLocation.EAST);
     }
  }
   // axis types (used to define which y-axis each curve is on)
   private static class YAxisId {}
 
   private class YGridSymbol extends GridSymbol {
     YGridSymbol() {
        super();
        setSymbolType(SymbolType.YGRIDLINE);
     }
  }
   private class YTickSymbol extends TickSymbol {
     YTickSymbol() {
        super();
        setSymbolType(SymbolType.BOX_WEST);
        setWidth(DEFAULT_TICK_LENGTH);
        setHeight(DEFAULT_TICK_THICKNESS);
        getAnnotation().setLocation(AnnotationLocation.WEST);
     }
  }

   // case-independent index of next "break" tag in string (case of HTML
   // returned from HasHTML.getHTML can change with browser)
   private static int indexOfBr(String s, int iStart) {
     final String BR1 = "<br>";
     final String BR2 = "<BR>";
     final String BR3 = "<li>"// recognize <li> as a break.
     final String BR4 = "<LI>";
     int iBr1 = s.indexOf(BR1, iStart);
     int iBr2 = s.indexOf(BR2, iStart);
     int iBr3 = s.indexOf(BR3, iStart);
     int iBr4 = s.indexOf(BR4, iStart);
     int result1 = 0;
     int result2 = 0;
     int result = 0;

     if (-1 == iBr1)
        result1 = iBr2;
     else if (-1 == iBr2)
        result1 = iBr1;
     else
        result1 = Math.min(iBr1, iBr2);

     if (-1 == iBr3)
        result2 = iBr4;
     else if (-1 == iBr4)
        result2 = iBr3;
     else
        result2 = Math.min(iBr3, iBr4);
    
     if (-1 == result1)
        result = result2;
     else if (-1 == result2)
        result = result1;
     else
        result = Math.min(result1, result2);

     return result;
    
   }
   private static int indexOfBr(String s) {
      return indexOfBr(s, 0);
   }
  
// Provides a character-based width estimate when simple tags
// such as <b> and <i> are present in a multi-line,
// <br>-delimited, string. Very approximate, but a useful
// default.
private static int htmlWidth(String sIn) {
   int iBr = indexOfBr(sIn);
   String s = (-1 == iBr) ? sIn : sIn.substring(0, iBr);
   final String LITERAL_PAT = "[&][#a-zA-Z]+[;]";
   s = s.replaceAll(LITERAL_PAT, "X"); // literals count as 1 char
   final String TAG_PAT = "[<][/a-zA-Z]+[>]";
   s = s.replaceAll(TAG_PAT, "");   // tags don't count at all
   return s.length();
}

// number of <br> delimited lines in an HTML string
private static int htmlHeight(String s) {
   final int BR_LEN = "<br>".length();
   int iBr = 0;
   int result = 1;
   if (null != s) {
     for (iBr = indexOfBr(s);
          iBr != -1;
          iBr = indexOfBr(s, iBr+BR_LEN))
        result++;
   }
   return result;

}

  /*
   * Annotates (labels) a chart symbol. Users access this
   * class via wrapper methods of the Point class, and via
   * various tick-label related methods of the Axis class.
   *
   */
 
   static class Annotation {
     String fontColor = DEFAULT_FONT_COLOR;
     int fontSize = DEFAULT_ANNOTATION_FONTSIZE;
     String fontStyle = "normal";
     String fontWeight = "normal";
     AnnotationLocation location = AnnotationLocation.SOUTH;
     String text;
     Widget widget;    // may be used in lieu of text or HTML
     boolean isVisible = true;
     int xShift = 0;
     int yShift = 0;
     boolean isHTML = false; // no break tags ==> plain text
     // Estimated number of lines, width in chars, of annotation
     // text (not used by Widgets)
     int numberOfLinesHigh = 0;
     int numberOfCharsWide = 0;
     int widthUpperBound = GChart.NAI;
     int heightUpperBound = GChart.NAI;
     static final int HTML_LEN = "<html>".length();
     static final int BR_LEN = "<br>".length();
     /*
      * Computes parameters used to estimate the width and height
      * of the (invisible) enclosing 1x1 Grid of an annotation
      * (used to align, center, etc. the annotation) <p>
      *
      */
     private String analyzeHTML(String s) {
       String result = null;   
       if (null == s) {
          isHTML = false;
          numberOfLinesHigh = 0;
          numberOfCharsWide = 0;
       }
       else if (!s.startsWith("<html>")) { // no html==>plain text
          isHTML = false;
          numberOfLinesHigh = 1;
          numberOfCharsWide = s.length();
          result = s;
       }
       else // HTML
          isHTML = true;
          // <html> is just a flag, not a tag, so strip it out.
          result = s.substring(HTML_LEN);
          if (widthUpperBound == GChart.NAI)
             numberOfCharsWide = htmlWidth(result);
                       
          if (heightUpperBound == GChart.NAI)
             numberOfLinesHigh = htmlHeight(result);
            
       }
       return result;
      
     }
     // Returns number of chars in first <br>-delimited line of
     // given string. A very crude way to estimate (especially
     // HTML) width in characters, but user can give explicit
     // widths when the width estimates based on this char width
     // heuristic fail them.
     static int getNumberOfCharsWide(String s) {
        int result = 0;
        if (!s.startsWith("<html>")) {
           result = s.length();
        }
        else {
            result = htmlWidth(s);
        }
        return result;
     }    
     public String getFontColor() {
       return fontColor;
     }
     public int getFontSize() {
        return fontSize;
     }
    
     public AnnotationLocation getLocation() {
        return location;
     }

    
     boolean isHTML() {return isHTML;}

     /*
      * Maps the pie slice bisecting angle and specified
      * annotation location into an appropriately transformed,
      * angle-aware, annotation location.
      *
      */
     AnnotationLocation getPieLocation(double thetaMid) {
            
       return location.decodePieLocation(thetaMid);                           
     }

     public String getText() {
        return (isHTML? ("<html>" + text) : text);
     }

     public boolean getVisible() {
       return isVisible;
     }
     public int getXShift() {
       return xShift;
     }
     public int getYShift() {
       return yShift;
     }
    
     public void setFontColor(String cssColor) {
        this.fontColor = cssColor;
     }
     public void setFontSize(int fontSize) {
        this.fontSize = fontSize;
     }
     public void setFontWeight(String cssWeight) {
        this.fontWeight = cssWeight;
     }
     public void setFontStyle(String cssStyle) {
        this.fontStyle = cssStyle;
     }

     String getFontWeight() { return fontWeight; }
     String getFontStyle() { return fontStyle; }

     public void setLocation(AnnotationLocation location) {
        this.location = location;
     }

     public void setText(String text, int widthUpperBound,
                         int heightUpperBound) {
        this.widthUpperBound = widthUpperBound;
        this.heightUpperBound = heightUpperBound;
        this.text = analyzeHTML(text);
        this.widget = null;
     }
     public void setText(String text) {
        setText(text, GChart.NAI, GChart.NAI);
     }

     public void setVisible(boolean isVisible) {
        this.isVisible = isVisible;
     }

     public void setWidget(Widget widget, int widthUpperBound,
                         int heightUpperBound) {
        this.widthUpperBound = widthUpperBound;
        this.heightUpperBound = heightUpperBound;
        this.text = null;
        this.widget = widget;
     }
     public void setWidget(Widget widget) {
       setWidget(widget, DEFAULT_WIDGET_WIDTH_UPPERBOUND,
                 DEFAULT_WIDGET_HEIGHT_UPPERBOUND);
   }
     public Widget getWidget() {return widget;}
    
     public void setXShift(int xShift) {
        this.xShift = xShift;
     }
     public void setYShift(int yShift) {
        this.yShift = yShift;
     }
    
     int getHeightUpperBound() {
        int result = 0;
        if (heightUpperBound != GChart.NAI)
           result = heightUpperBound;
        else {
           result = (int) Math.ceil(fontSize *
                                    numberOfLinesHigh *
                                    CHARHEIGHT_TO_FONTSIZE_UPPERBOUND);
        }
        return result;
     }
     HasHorizontalAlignment.HorizontalAlignmentConstant
           getHorizontalAlignment() {
        return location.getHorizontalAlignment();
     }
     int getUpperLeftX(int x, int symbolWidth) {
        int result = location.getUpperLeftX(
           x, getWidthUpperBound(), symbolWidth);
        return result;
     }
     int getUpperLeftY(int y, int symbolHeight) {
        int result = location.getUpperLeftY(
          y, getHeightUpperBound(), symbolHeight);
        return result;
     }
     HasVerticalAlignment.VerticalAlignmentConstant
           getVerticalAlignment() {
        return location.getVerticalAlignment();
     }
     int getWidthUpperBound() {
        int result = 0;
        if (widthUpperBound != GChart.NAI)
           result = widthUpperBound;
        else {
           result = (int) Math.ceil(fontSize *
                    numberOfCharsWide * CHARWIDTH_TO_FONTSIZE_UPPERBOUND);
        }
        return result;
     }
    
  } // end of class Annotation

   /**
    ** A special value used to tell GChart that a property should
    ** be defined via CSS, not via an explicit Java API specification.
    **
    ** <p>
    **
    ** This value is mainly used by GChart's "CSS convenience methods"
    ** which make it possible to use the Java API to specify
    ** certain properties of a GChart that can also be specified
    ** via CSS. When the value of the Java property is set to
    ** <tt>USE_CSS</tt> GChart ignores the API specification
    ** and allows the standard "CSS cascade" to define the
    ** property.
    **
    ** The discussion below elaborates on why GChart
    ** supports CSS convenience methods, and how the <tt>USE_CSS</tt>
    ** keyword fits into that support.
    **
    ** <p>
    **
    ** <blockquote><small>
    ** <b>CSS Convenience Methods and the
    ** <tt>GChart.USE_CSS</tt> keyword</b>
    ** <p>
    **
    ** Like all GWT Widgets, a GChart is both an object in a Java application,
    ** and an HTML element in a web page.
    **
    ** <p>
    ** This duality naturally divides the properties of a GChart into
    ** three categories:
    **
    ** <ol>
    **
    **  <li>Those you can access only via the Java API.
    **  <li>Those you can access only via CSS and the DOM.
    **  <li>Those you can access both ways.
    ** </ol>
    ** <p>
    **
    ** I've used the following criteria to determine the access method
    ** appropriate for a given GChart property:
    ** <p>
    **
    ** <ol>
    **
    ** <li> Those properties that mainly define the chart
    **      itself--independent of its relationship to any containing web
    **      page--are exclusively accessed via the Java API
    **
    ** <p>
    **
    ** For example, the x,y data values of a curve have everything to do
    ** with the chart itself and nothing to do with the enclosing web
    ** page, so all the defining x,y data of a curve can only be
    ** accessed via the Java API.
    ** <p>
    **
    ** <li>Those properties that mainly define how the chart fits into
    ** the enclosing web page and have nothing to do with the chart
    ** itself are accessed exclusively via CSS stylesheets or the
    ** GWT DOM class.
    **
    ** <p> For example, how big of a margin should be placed around a
    ** GChart is only about how the GChart fits into the enclosing web
    ** page, so you must define a GChart's margins using a CSS
    ** stylesheet (or via the GWT DOM class)--there is no
    ** <tt>GChart.setMargin</tt> method.
    **
    ** <li>Finally, those properties that, in some situations are
    ** best viewed as part of the chart itself, and in other
    ** situations as defining how the chart fits into the enclosing
    ** web page can be accessed <i>either</i> via the Java API, or
    ** via CSS/DOM.  <p>
    **
    ** For example, if you are focused on assuring that the chart has
    ** a the same border as every other element on the page, the
    ** border around the chart as a whole can be viewed as relating
    ** to how the chart fits into the enclosing web page. On the
    ** other hand, if you are focused on assuring that, like the
    ** frame around a picture, the border looks good around that
    ** particular chart, it makes more sense to view it as a part of
    ** the chart itself.
    **
    ** </ol>
    **
    ** <p>
    **
    ** The Java API methods for GChart properties in this third
    ** category are known as "CSS convenience methods" because, though
    ** you could do the same thing by exploiting the "GChart as HTML
    ** element" perspective, these methods save you the trouble of
    ** looking up CSS syntax, splitting up your chart's specification
    ** between Java code and a CSS stylesheet, invoking a rather
    ** hard-to-remember method call in the GWT DOM class, etc.
    **
    ** <p> Specifications made via the GChart Java API always take
    ** precedence over those made via stylesheets or the DOM class.
    ** To instruct GChart that you want one of these properties to be
    ** defined via CSS or the DOM, set the associated Java API
    ** property to the special value <tt>GChart.USE_CSS</tt>.
    **
    ** <p> Fortunately, since <tt>USE_CSS</tt> is the default value for
    ** every one of these CSS convenience properties, if you never use the
    ** Java API to set them, you can use CSS to control them
    ** just as you would for a GWT widget that did not support
    ** convenience properties.
    **
    **
    ** Unfortunately, these CSS defaults rarely produce a great
    ** looking chart out of the box; the example CSS snippet
    ** below defines all of these convenience properties and
    ** attaches them to GChart's default CSS selector (aka
    ** stylename) in a way that I think looks better. A comment
    ** to the right of each line contains the corresponding
    ** CSS convenience-method call that has the same effect.
    **
    ** <p>
    ** <pre>
    ** .gchart-GChart {
    **   background-color: #DDF;   /* setBackgroundColor("#DDF"); *&#47;
    **   border-width: 1px;         /* setBorderWidth("1px"); *&#47;
    **   border-color: black;       /* setBorderColor("black"); *&#47;
    **   border-style: solid;       /* setBorderStyle("solid");  *&#47;
    **   font-family: Arial, sans-serif;  /* setFontFamily("Arial, sans-serif"); *&#47;
    ** }
    ** </pre>
    **
    **
    ** Note that certain CSS convenience methods that could in
    ** principle have been added, such as those for defining the
    ** background image of a chart, were omitted because I
    ** thought they would almost never be used. Of course, you
    ** can always access these CSS properties "the old fashioned
    ** way" (via a CSS specification or methods of the GWT DOM class).
    **
    ** <p>
    ** </small></blockquote>
    **
    ** @see #setBorderColor(String) setBorderColor
    ** @see #setBorderStyle(String) setBorderStyle
    ** @see #setBackgroundColor(String) setBackgroundColor
    ** @see #setBorderWidth(String) setBorderWidth
    ** @see #setFontFamily(String) setFontFamily
    **
    **
    **
    **/
    /*
     * Setting a CSS property to "" generally clears the
     * attribute specification, restoring things to their initial
     * defaults (not sure if this always works, but it appears to
     * so far).
     *
     */
    public final static String USE_CSS = "";
   /**
    ** In analogy to how it uses <tt>Double.NaN</tt> (Not a
    ** Number), GChart uses <tt>GChart.NAI</tt> (Not an Integer) to
    ** represent integers whose values have not been explicitly
    ** specified.
    **
    **/
   public static final int NAI = Integer.MIN_VALUE;

   /** Default size, in pixels, of text used to annotate individual
    ** plotted points on a curve.
    **
    ** @see Curve.Point#setFontSize Point.setFontSize
     */
   public static final int DEFAULT_ANNOTATION_FONTSIZE = 12;
   /** Default color of border around the chart legend
    **
    ** @see #setLegendBorderColor setLegendBorderColor
    **
    **/
   public static final String DEFAULT_LEGEND_BORDER_COLOR = "black";
   /** Default width of border around the chart legend
    **
    ** @see #setLegendBorderWidth setLegendBorderWidth
    **
    **/
   public static final int DEFAULT_LEGEND_BORDER_WIDTH = 1;
   /** Default style of border around the chart legend
    **
    ** @see #setLegendBorderStyle setLegendBorderStyle
    **
    **/
   public static final String DEFAULT_LEGEND_BORDER_STYLE = "solid";

   /** Default color of background of the chart legend
    **
    ** @see #setLegendBackgroundColor setLegendBackgroundColor
    **
     */
   public static final String DEFAULT_LEGEND_BACKGROUND_COLOR = "transparent";
  /**
   ** The default color of any text appearing in a chart's
   ** legend, annotations, or tick labels.
   **
   ** @see #setLegendFontColor setLegendFontColor
   ** @see Axis#setTickLabelFontColor setTickLabelFontColor
   ** @see Curve.Point#setAnnotationFontColor setAnnotationFontColor
   **
   **/
   public final static String DEFAULT_FONT_COLOR ="black";
   /**
    ** Default style of axis label and legend fonts.
    **
    ** @see #setLegendFontStyle setLegendFontStyle
    ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
    ** @see Curve.Point#setAnnotationFontStyle
    ** setAnnotationFontStyle
    **
    **/
   public static final String DEFAULT_FONT_STYLE = "normal";
   /** Default weight of axis label and legend fonts.
    **
    ** @see #setLegendFontWeight setLegendFontWeight
    ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
    ** @see Curve.Point#setAnnotationFontWeight
    ** setAnnotationFontWeight
    **
    **/
  
   public static final String DEFAULT_FONT_WEIGHT = "normal";

/**
    ** The default template string used to generate the hovertext
    ** displayed when the user hovers their mouse above a point
    ** on a curve (pie slices have a different default).
    **
    ** @see Symbol#setHovertextTemplate setHovertextTemplate
    ** @see #DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
    **    DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE
    **   
    */
   public static final String DEFAULT_HOVERTEXT_TEMPLATE = "(${x}, ${y})";
   /** The default fontsize of text that appears
    ** in the chart's legend (key).
    **
    ** @see Axis#setTickLabelFontSize setTickLabelFontSize
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **
    **/
   public final static int DEFAULT_LEGEND_FONTSIZE = 12;


   /**
    ** The default background color used for the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
    **
    **/
   public final static String DEFAULT_PLOTAREA_BACKGROUND_COLOR = "transparent";
   /**
    ** The default border color used for the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
    **
    **/
   public final static String DEFAULT_PLOTAREA_BORDER_COLOR = "black";
   /**
    ** The default style of the border around the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
    **
    **/
   public final static String DEFAULT_PLOTAREA_BORDER_STYLE = "solid";
   /**
    ** The default width of the border around the chart's plot area
    ** if none is specified.
    **
    ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
    **
    **/
   public final static int DEFAULT_PLOTAREA_BORDER_WIDTH = 0;
   /**
    ** The default CSS background color used for symbols if none is
    ** specified.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBackgroundColor setBackgroundColor
    **/
   public static final String DEFAULT_SYMBOL_BACKGROUND_COLOR =
      "transparent";
   /**
    ** The default CSS border colors used for symbols if none are
    ** specified. These defaults are, in order of the curve's
    ** integer index: red, green, blue, fuchsia, aqua, teal,
    ** maroon, lime, navy, silver, olive, purple.  This sequence
    ** repeats if there are more than 12 curves.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderColor setBorderColor
    **
    **/
   public static final String[] DEFAULT_SYMBOL_BORDER_COLORS =
     {"red", "green", "blue",
      "fuchsia", "aqua", "teal",
      "maroon", "lime", "navy",
      "silver", "olive", "purple"};
   /**
    ** The default CSS border style used for symbols if none is
    ** specified; this default is "solid".
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderStyle setBorderStyle
    **
    **/
   public static final String DEFAULT_SYMBOL_BORDER_STYLE = "solid";
   /**
    ** The default CSS border width used for symbols if none is
    ** specified; this default is 1 pixel.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setBorderWidth setBorderWidth
    **
    **/
   public static final int DEFAULT_SYMBOL_BORDER_WIDTH = 1;
   /**
    ** The default spacing between discrete, rectangular, elements
    ** used to simulate continous graphical objects, such as
    ** connecting lines in line charts or a shaded pie slice.
    ** <p>
    ** Note that if fill thickness is 0 (the default for all
    ** symbols except pie slices) no filling will be performed,
    ** regardless of the fill spacing.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final double DEFAULT_SYMBOL_FILL_SPACING = 4;
   /**
    ** The default "thickness" of the rectangular elements used to
    ** simulate continous graphical objects, such as connecting
    ** lines in line charts. This default applies to all symbol
    ** types <tt>except</tt> for those representing pie slices,
    ** whose default is
    ** <tt>DEFAULT_PIE_SLICE_FILL_THICKNESS</tt>, and the LINE
    ** symbol type, whose default is DEFAULT_LINE_FILL_THICKNESS.
    **
    ** <p> Since this default thickness is 0 px, this implies
    ** that, except for pie slices and lines, no such continuous fill
    ** elements are generated by default. For example, if you
    ** want to have dotted connecting lines drawn between individual
    ** data points represented using the <tt>BOX_CENTER</tt>
    ** symbol type, you must explicitly specify a positive fill
    ** thickness (for solid connecting lines, the LINE symbol
    ** is usually far more efficient than using a fill thickness
    ** of 1px with the BOX_CENTER symbol).
    **
    ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
    **      DEFAULT_PIE_SLICE_FILL_THICKNESS
    ** @see #DEFAULT_LINE_FILL_THICKNESS
    **      DEFAULT_LINE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_SYMBOL_FILL_THICKNESS = 0;
  
   /**
    ** The default thickness of connecting lines drawn on
    ** curves whose symbols have the LINE symbol type.
    **
    ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
    **      DEFAULT_SYMBOL_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_LINE_FILL_THICKNESS = 1;
   private static final int DEFAULT_LINE_FILL_SPACING = 1;

   /**
    ** The default "spacing" between corresponding edges of the
    ** rectangular elements used to simulate continuous fill of pie
    ** slices.  <p>
    **
    ** @see #DEFAULT_SYMBOL_FILL_SPACING
    **      DEFAULT_SYMBOL_FILL_SPACING
    ** @see #DEFAULT_PIE_SLICE_FILL_THICKNESS
    **      DEFAULT_PIE_SLICE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final double DEFAULT_PIE_SLICE_FILL_SPACING = 4;
   /**
    ** The default "thickness" of the rectangular elements
    ** used to simulate continous fill of pie slices. This
    ** thickness defines the height of horizontal pie slice
    ** shading bars, and the width of vertical shading bars.
    ** <p>
    **
    ** @see #DEFAULT_SYMBOL_FILL_THICKNESS
    **      DEFAULT_SYMBOL_FILL_THICKNESS
    ** @see #DEFAULT_LINE_FILL_THICKNESS
    **      DEFAULT_LINE_FILL_THICKNESS
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setFillSpacing setFillSpacing
    ** @see Symbol#setFillThickness setFillThickness
    **
    **/
   public static final int DEFAULT_PIE_SLICE_FILL_THICKNESS = 2;

   /**
    ** The default hovertext template used by symbols when they have a
    ** symbol type of of the form PIE_SLICE_*.
    **
    ** @see Symbol#setHovertextTemplate setHovertextTemplate
    ** @see SymbolType#PIE_SLICE_OPTIMAL_SHADING PIE_SLICE_OPTIMAL_SHADING
    ** @see #DEFAULT_HOVERTEXT_TEMPLATE DEFAULT_HOVERTEXT_TEMPLATE
    **
    **/
   public static final String DEFAULT_PIE_SLICE_HOVERTEXT_TEMPLATE =
     "${pieSliceSize}";
   /**
    ** The default height (including borders) used for
    ** symbols if none is specified; this default is
    ** the same as for <tt>DEFAULT_SYMBOL_WIDTH</tt>
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setHeight setHeight
    ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
    **
    **/
   public static final int DEFAULT_SYMBOL_HEIGHT = 7;
  
/**
    ** The default symbol type for curve if none is
    ** specified; this default is BOX_CENTER
    **
    ** @see SymbolType#BOX_CENTER BOX_CENTER
    ** @see Symbol#setSymbolType setSymbolType
    **
    **/
   public static final SymbolType DEFAULT_SYMBOL_TYPE = SymbolType.BOX_CENTER;

  /**
    ** The default width (including borders) used for
    ** symbols if none is specified.
    **
    ** @see Curve#getSymbol getSymbol
    ** @see Symbol#setWidth setWidth
    ** @see #DEFAULT_SYMBOL_WIDTH DEFAULT_SYMBOL_WIDTH
    **/
   public static final int DEFAULT_SYMBOL_WIDTH =
     DEFAULT_SYMBOL_HEIGHT;
  
   /**
   * The default number of tick marks on each Axis.
   *
   * @see Axis#setTickCount setTickCount
   *
   */
  public static final int DEFAULT_TICK_COUNT = 10;

  /** The default color (a CSS color specification) of tick labels
   **
   ** @see Axis#setTickLabelFontColor setTickLabelFontColor
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_COLOR ="black";

  /** The default CSS font-style applied to tick labels
   **
   ** @see Axis#setTickLabelFontStyle setTickLabelFontStyle
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_STYLE ="normal";

  /** The default CSS font-weight applied to tick labels
   **
   ** @see Axis#setTickLabelFontWeight setTickLabelFontWeight
   ** @see #getXAxis getXAxis
   ** @see #getYAxis getYAxis
   ** @see #getY2Axis getY2Axis
   **/
   public final static String DEFAULT_TICK_LABEL_FONT_WEIGHT ="normal";

  /** The default fontsize (in pixels) of tick labels
    **
    ** @see Axis#setTickLabelFontSize setTickLabelFontSize
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **/
   public final static int DEFAULT_TICK_LABEL_FONTSIZE = 12;
  /**
    ** The default GWT <tt>NumberFormat</tt> format string used to convert
    ** numbers to the text strings displayed as tick labels
    ** on X, Y, and Y2 axes.
    **
    ** @see Axis#setTickLabelFormat setTickLabelFormat
    ** @see #getXAxis getXAxis
    ** @see #getYAxis getYAxis
    ** @see #getY2Axis getY2Axis
    **
    **/
   public final static String DEFAULT_TICK_LABEL_FORMAT = "#.##";

   /**
   * The default length of tick marks, in pixels.
   *
   * @see Axis#setTickLength setTickLength
   */
  public static final int DEFAULT_TICK_LENGTH = 6;


   /**
   * The default thickness of tick marks, in pixels.
   *
   * @see Axis#setTickThickness setTickThickness
   */
  public static final int DEFAULT_TICK_THICKNESS = 1; // pixel

  /**
   ** The default upper bound on the width of widgets used
   ** in annotations and tick labels that GChart
   ** will assume for centering and similar alignment purposes.
   **
   ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
   ** @see Axis#addTick(double,Widget,int,int) addTick
   **
   **/
  public static final int DEFAULT_WIDGET_WIDTH_UPPERBOUND = 200;
  /**
   ** The default upper bound on the height of widgets used
   ** in annotations and tick labels that GChart
   ** will assume for centering and similar alignment purposes.
   **
   ** @see Curve.Point#setAnnotationWidget setAnnotationWidget
   ** @see Axis#addTick(double,Widget,int,int) addTick
   **
   **/
  public static final int DEFAULT_WIDGET_HEIGHT_UPPERBOUND = 100;
 
  /**
   *  The default width of the area of the chart in
   *  which curves are displayed, in pixels.
   */
  public final static int DEFAULT_X_CHARTSIZE = 300; // pixels
  /**
   * The default height of the area of the chart in
   * which curves are displayed, in pixels.
   */
  public final static int DEFAULT_Y_CHARTSIZE = 300; // pixels

  /**
   ** Keyword used to indicate that a curve should be
   ** displayed on the left y-axis.
   **
   ** @see #Y2_AXIS Y2_AXIS
   ** @see GChart.Curve#setYAxis(GChart.YAxisId) setYAxis
   **
   **/
   public static final YAxisId Y_AXIS = new YAxisId();

   /**
   ** Keyword used to indicate that a curve should be
   ** displayed on the right (the so-called y2) y-axis.
   **
   ** @see #Y_AXIS Y_AXIS
   ** @see Curve#setYAxis setYAxis
   **
   **/
   public static final YAxisId Y2_AXIS = new YAxisId();

  /**
   ** The default URL GChart will use to access the blank image
   ** (specifically, a 1 x 1 pixel transparent GIF) it requires
   ** to prevent "missing image" icons from appearing in your
   ** charts.
   ** 
   ** @see #setBlankImageURL setBlankImageURL
   **
   **/
  public final static String DEFAULT_BLANK_IMAGE_URL = "gchart.gif";
  private static final int DEFAULT_GRID_HEIGHT =
      DEFAULT_TICK_THICKNESS;  
  private static final int DEFAULT_GRID_WIDTH =
      DEFAULT_TICK_THICKNESS;  
  private static final String GRID_BORDER_STYLE = "none";
  private static final int GRID_BORDER_WIDTH = 0;

  /** The default color used for all axes, gridlines, and ticks.
   **
   ** @see #setGridColor setGridColor
   **
   */
  public static final String DEFAULT_GRID_COLOR = "black";


  /** The default thickness (height) of the rectangular region at
   ** the bottom of the chart allocated for footnotes, per
   ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This
   ** default is only used when the footnote thickness is set to
   ** <tt>GChart.NAI</tt> (the default).
   **
   ** @see #setChartFootnotesThickness setChartFootnotesThickness
   **
   */
  public static final int DEFAULT_FOOTNOTES_THICKNESS = 15;

  /**
   **
   ** The default thickness (height) of the rectangular region at
   ** the top of the chart allocated for the chart's title, per
   ** <tt>&lt;br&gt;</tt> or <tt>&lt;li&gt;</tt> delimited HTML line. This default
   ** is only used when the title thickness is set to
   ** <tt>GChart.NAI</tt>.
   **
   **
   ** @see #setChartTitleThickness setChartTitleThickness
   **
   */
  public static final int DEFAULT_TITLE_THICKNESS = 15;

 
 
// for purposes of estimating fixed space "band" around the plot
// panel reserved for the tick labels:
  private static final double
      TICK_CHARHEIGHT_TO_FONTSIZE_LOWERBOUND = 1.0;  
  // a bit larger than the 0.6 rule-of-thumb
  private static final double
      TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND = 0.7;
  // For estimating size of invisible "box" needed for alignment
  // purposes.  Note: when these are bigger, annotations remain
  // properly aligned longer as user zooms up font sizes.  But,
  // bigger bounding boxes can slow updates (not sure why,
  // maybe it's related to hit testing browser has to do)
  private static final double
        CHARHEIGHT_TO_FONTSIZE_UPPERBOUND = 2*1.5;
  private static final double
       CHARWIDTH_TO_FONTSIZE_UPPERBOUND = 2*0.7;

  private static final String TICK_BORDER_STYLE = GRID_BORDER_STYLE;
  private static final int TICK_BORDER_WIDTH = GRID_BORDER_WIDTH;

  private static void setBackgroundColor(
      UIObject uio, String cssColor) {
     DOM.setStyleAttribute(uio.getElement(),
                            "backgroundColor", cssColor);
  }

//  private static void setBackground(
//      UIObject uio, String cssColor) {
//     DOM.setStyleAttribute(uio.getElement(),
//                           "background", cssColor);
//  }
  
  private static void setBorderColor(
       UIObject uio, String cssColor) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderColor", cssColor);
   }




 
  private static void setBorderStyle(
       UIObject uio, String cssBorderStyle) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderStyle", cssBorderStyle);
   }
 

  private static void setBorderWidth(
       UIObject uio, String cssBorderWidth) {
      DOM.setStyleAttribute(uio.getElement(),
                            "borderWidth", cssBorderWidth);
   }

  private static void setBorderWidth(
      UIObject uio, int borderWidth) {
      if (borderWidth != GChart.NAI)
         setBorderWidth(uio, borderWidth + "px");
      else
         setBorderWidth(uio, "");
   }

  private static void setFontFamily(
       UIObject uio, String cssFontFamily) {
      DOM.setStyleAttribute(uio.getElement(),
                            "fontFamily", cssFontFamily);
  
 
  private static void setFontSize(
       UIObject uio, int fontSize) {
      DOM.setIntStyleAttribute(
         uio.getElement(), "fontSize", fontSize);
   }

  private static void setFontStyle(
       UIObject uio, String fontStyle) {
      DOM.setStyleAttribute(uio.getElement(),
         "fontStyle", fontStyle);
   }
  private static void setFontWeight(
       UIObject uio, String fontWeight) {
      DOM.setStyleAttribute(uio.getElement(),
         "fontWeight", fontWeight);
   }

  private static void setColor(
       UIObject uio, String cssColor) {
      DOM.setStyleAttribute(uio.getElement(),
         "color", cssColor);
  }

  // valid layout strings are fixed, auto, and inherit
//  private static void setTableLayout(
//       UIObject uio, String layout) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "table-layout", layout);
//  }
 

 
//   private static void setLineHeight(
//       UIObject uio, String cssLineHeight) {
//      DOM.setStyleAttribute(uio.getElement(),
//                            "lineHeight", cssLineHeight);
//   }

  //   private static void setTextAlign(
//       UIObject uio, String cssTextAlign) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "textAlign", cssTextAlign);
//   }
// 
//   private static void setMargin(
//       UIObject uio, String cssMargin) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "margin", cssMargin);
//   }
   private static void setPadding(
       UIObject uio, String cssPadding) {
      DOM.setStyleAttribute(uio.getElement(), "padding", cssPadding);
   }
   // valid choices are block, inline, list-item, or none
//   private static void setDisplay(
//       UIObject uio, String cssDisplay) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "display", cssDisplay);
//   }
   // choices are: visible, hidden, scroll or auto
   private static void setOverflow(
       UIObject uio, String cssOverflow) {
      DOM.setStyleAttribute(
         uio.getElement(), "overflow", cssOverflow);
   }
//   private static void setTextLeading(
//       UIObject uio, String cssTextLeading) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "textTrailing", cssTextLeading);
//   }
//   private static void setVerticalAlign(
//       UIObject uio, String cssVerticalAlign) {
//      DOM.setStyleAttribute(
//         uio.getElement(), "verticalAlign", cssVerticalAlign);
//   }

   // returns the sign of the given number
   static int sign(double x) {
       int result = (x < 0) ? -1 : 1;
       return result;
   }

  // Validates multipliers used to simplify computing the
   // upper left corner location of symbols and labels to
   // properly reflect their alignment relative to the
   // plotted point or labeled symbol.
  static void validateMultipliers(
      int widthMultiplier, int heightMultiplier) {
      if (!(widthMultiplier == 0 || Math.abs(widthMultiplier)==1) &&
          !(heightMultiplier == 0 ||Math.abs(heightMultiplier)==1))
         throw new IllegalArgumentException(
          "widthMultiplier, heightMultiplier args must both be " +
          "either 0, 1, or -1");
  }

// is value within given limits, inclusive?
  static boolean withinRange(double x, double minLim, double maxLim) {
     // x!=x is a faster isNaN; NaN is considered in range
     boolean result = (x!=x) ? true : (x >= minLim && x <= maxLim);
     return result;
  }
 
  private Widget chartFootnotes;
  private boolean chartFootnotesLeftJustified;

// outer container needed so CSS-defined paddings don't interfere with positioning 
  private SimplePanel chartPanel = new SimplePanel();

  private String borderWidth = USE_CSS;
  private String borderStyle = USE_CSS;
  private String borderColor = USE_CSS;
  private String backgroundColor = USE_CSS;
  private static String blankImageURL = null;
  boolean chartDecorationsChanged = true;
  private Widget chartTitle;
  // collection of curves associated with this chart. 
  private ArrayList curves = new ArrayList();
  private String gridColor = DEFAULT_GRID_COLOR;
  private String fontFamily = USE_CSS;
  private int footnotesThickness = GChart.NAI;
  private String legendBackgroundColor =
                             DEFAULT_LEGEND_BACKGROUND_COLOR;
  private String legendBorderColor = DEFAULT_LEGEND_BORDER_COLOR;
  private int legendBorderWidth = DEFAULT_LEGEND_BORDER_WIDTH;
  private String legendBorderStyle = DEFAULT_LEGEND_BORDER_STYLE;
  private int legendThickness = GChart.NAI;
 
  private boolean isLegendVisible = true;
  private String legendFontColor = DEFAULT_FONT_COLOR;
  private int legendFontSize = DEFAULT_LEGEND_FONTSIZE;
  private String legendFontStyle = DEFAULT_FONT_STYLE;
  private String legendFontWeight = DEFAULT_FONT_WEIGHT;

/*
  * Contains the plotting region, as well as axes, ticks, and
  * tick-labels associated with that region. Note that tickText
  * must be centered on the ticks--placing them on the same
  * AbsolutePanel as the ticks/plots facilitates this.
  *
  */
  PlotPanel plotPanel =  new PlotPanel();
  private String padding = USE_CSS;
  private boolean optimizeForMemory = false;
  private String plotAreaBackgroundColor = DEFAULT_PLOTAREA_BACKGROUND_COLOR;;
  private String plotAreaBorderColor = DEFAULT_PLOTAREA_BORDER_COLOR;
  private String plotAreaBorderStyle = DEFAULT_PLOTAREA_BORDER_STYLE;
  private int plotAreaBorderWidth = DEFAULT_PLOTAREA_BORDER_WIDTH;
  private boolean showOffChartPoints = false;
  private boolean showOffDecoratedChartGlyphs = true;
  private int titleThickness = GChart.NAI;
  private Axis xAxis = new XAxis();
  private int xChartSize; //  pixel size of plotting region


  private Axis y2Axis = new Y2Axis();

  private Axis yAxis = new YAxis();

  private int yChartSize;

  /**
   * Convenience no-arg constructor equivalent to
   * <tt>GChart(DEFAULT_X_CHARTSIZE,DEFAULT_Y_CHARTSIZE)</tt>.
   *
   * @see #GChart(int,int) GChart(int,int)
   *
   */
  public GChart() {
     this(DEFAULT_X_CHARTSIZE, DEFAULT_Y_CHARTSIZE);
  }

  // index of curve that holds correspondingly-named chart part
  // (sys curve indexes are negative)
  private static int N_SYSTEM_CURVES = 6;
  private final static int TITLE_ID = 0-N_SYSTEM_CURVES;
  private final static int YLABEL_ID = 1-N_SYSTEM_CURVES;
  private final static int Y2LABEL_ID = 2-N_SYSTEM_CURVES;
  private final static int LEGEND_ID = 3-N_SYSTEM_CURVES;
  private final static int XLABEL_ID = 4-N_SYSTEM_CURVES;
  private final static int FOOTNOTES_ID = 5-N_SYSTEM_CURVES;
// flag to assure only GChart can access these internal curves
// (accidental negative-index curve tweaks could be very confusing)
  private boolean systemCurvesAccessible = false;
 
  // adds system curves that hold chart decorations (title, etc.)
  private void addDecorationHoldingCurves() {
     systemCurvesAccessible = true;  
     final SymbolType[] anchors = {
        SymbolType.ANCHOR_NORTHWEST, // TITLE_ID
        SymbolType.ANCHOR_WEST,  // YLABEL_ID
        SymbolType.ANCHOR_EAST,  // Y2LABEL_ID
        SymbolType.ANCHOR_EAST,  // LEGEND_ID
        SymbolType.ANCHOR_SOUTH,  // XLABEL_ID
        SymbolType.ANCHOR_SOUTHWEST  // FOOTNOTES_ID
     };
    
     // add curves to hold the decorations around the chart
     for (int i = 0; i < N_SYSTEM_CURVES; i++) {
        addCurve(i - N_SYSTEM_CURVES); // indexes are sequential
        getCurve().getSymbol().setSymbolType(anchors[i]);
// actual position is irrelevant with anchor symbol types
        getCurve().addPoint(0,0);
        getCurve().getPoint(0).setAnnotationLocation(
           AnnotationLocation.CENTER);
        getCurve().getSymbol().setHovertextTemplate("");
     }
     // curve count should now be 0 (system curves don't count)
     if (getNCurves() != 0) throw new
        IllegalStateException("getNCurves() != 0. Probably a GChart bug.");

     systemCurvesAccessible = false;  

  }

  /*
   * Updates the system curves that represent chart
   * decorations (axis labels, title, etc.).<p>
   *
   * Note that all x, y shifts are relative to the "anchoring"
   * symbol type locations defined once and for all in the
   * addDecorationHoldingCurves method above.
   *
   */
  private void updateDecorations() {

    systemCurvesAccessible = true;  

    // x-axis label
    getCurve(XLABEL_ID).getPoint(0).setAnnotationWidget(
       getXAxis().getAxisLabel(), getXChartSize(),
       getXAxis().getAxisLabelThickness());
    getCurve(XLABEL_ID).getPoint(0).setAnnotationYShift(
      - getXAxis().getTickLabelThickness()
      - getXAxis().getTickLength()
      - getXAxis().getAxisLabelThickness()/2);

    // y-axis label
    getCurve(YLABEL_ID).getPoint(0).setAnnotationWidget(
       getYAxis().getAxisLabel(),
       getYAxis().getAxisLabelThickness(), getYChartSize());
    getCurve(YLABEL_ID).getPoint(0).setAnnotationXShift(
      - getYAxis().getTickLabelThickness()
      - getYAxis().getTickLength()
      - getYAxis().getAxisLabelThickness()/2);

    // y2-axis label
    getCurve(Y2LABEL_ID).getPoint(0).setAnnotationWidget(
       getY2Axis().getAxisLabel(),
       getY2Axis().getAxisLabelThickness(), getYChartSize());
    getCurve(Y2LABEL_ID).getPoint(0).setAnnotationXShift(
      + getY2Axis().getTickLabelThickness()
      + getY2Axis().getTickLength()
      + getY2Axis().getAxisLabelThickness()/2);

    // legend
    Grid legend = null;
    if (isLegendVisible() && 0 < getNVisibleCurvesOnLegend())
      legend = createLegend(plotPanel);
    getCurve(LEGEND_ID).getPoint(0).setAnnotationWidget(
       legend, getLegendThickness(), getYChartSize());
    getCurve(LEGEND_ID).getPoint(0).setAnnotationXShift(
      + getY2Axis().getTickLabelThickness()
      + getY2Axis().getTickLength()
      + getY2Axis().getAxisLabelThickness()
      + getLegendThickness()/2 );

    // title
    int shiftToLeftEdge =
       - getYAxis().getAxisLabelThickness()
       - getYAxis().getTickLabelThickness()
       - getYAxis().getTickLength();
    int shiftToHorizontalMidpoint =
         shiftToLeftEdge + getXChartSizeDecorated()/2;
    getCurve(TITLE_ID).getPoint(0).setAnnotationWidget(
       getChartTitle(), getXChartSizeDecorated(),
       getChartTitleThickness());
    getCurve(TITLE_ID).getPoint(0).setAnnotationYShift(
       getChartTitleThickness()/2);
    getCurve(TITLE_ID).getPoint(0).setAnnotationXShift(
       shiftToHorizontalMidpoint);

    // footnotes
    getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationWidget(
       getChartFootnotes(), getXChartSizeDecorated(),
       getChartFootnotesThickness());
    getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationYShift(
      - getXAxis().getTickLabelThickness()
      - getXAxis().getTickLength()
      - getXAxis().getAxisLabelThickness()
      - getChartFootnotesThickness()/2 );
    if (getChartFootnotesLeftJustified()) {
      getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
        shiftToLeftEdge);
      getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
           AnnotationLocation.EAST);
    }
    else { // footnotes centered
      getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationXShift(
        shiftToHorizontalMidpoint);
      getCurve(FOOTNOTES_ID).getPoint(0).setAnnotationLocation(
           AnnotationLocation.CENTER);
    }
   
    systemCurvesAccessible = false;  

  }
 
  /**
   * Instantiates a GChart with a curve display region of
   * the specified size.
   *
   *
   * @param xChartSize the width of the curve display region, in pixels.
   * @param yChartSize the height of the curve display region, in pixels.
   *
   * @see #setXChartSize setXChartSize
   * @see #setYChartSize setYChartSize
   * @see #setChartSize setChartSize
   */
  public GChart(int xChartSize, int yChartSize) {
     this.xChartSize = xChartSize;
     this.yChartSize = yChartSize;
     addDecorationHoldingCurves();
     // Note: plotPanel (where real chart resides) won't get
     // added to chartPanel (top-level do-nothing container for
     // padding and such) until AFTER first update; FF2 has some
     // serious performance problems otherwise for common usage
     // scenarios with large widget-count pages.
     initWidget(chartPanel);
     /*
      * See the block comment at top of "class GChart" for a
      * detailed discussion/rational for GChart's (very minimal
      * support) of stylenames. Would like deeper support if
      * I can ever figure out how to do it without hamstringing
      * future versions by locking them into a particular
      * implementation I might need to change later on.
      */
     setStyleName("gchart-GChart");
  }
/**
* Adds a new curve to the chart, at the end of the current
* list of curves.
* <p>
*
* @see #getCurve getCurve
* @see #addCurve(int) addCurve(int)
* @see #removeCurve removeCurve
* @see #clearCurves clearCurves
* @see #getNCurves getNCurves
*/


public void addCurve() {
addCurve(getNCurves());
}
  /**
   * Adds a new curve to the chart, at the specified position
   * in the curve sequence. Existing curves at postions at
   * or greater than the specified position have their
   * positional indexes increased by 1.
   * <p>
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #removeCurve removeCurve
   * @see #clearCurves clearCurves
   * @see #getNCurves getNCurves
   */

public void addCurve(int iCurve) {
  if (iCurve < 0 && !systemCurvesAccessible)
    throw new IllegalArgumentException(
       "iCurve = " + iCurve +"; iCurve may not be negative.");
                                             
  Curve c = new Curve();
  curves.add(iCurve+N_SYSTEM_CURVES, c);
  // invalidate widgets rendering this curve, and every curve
  // that comes after it.
  if (iCurve < getNCurves()-1) {
     // added before existing curve, invalidate this and
     // all subsequent curves
    c.getWidgetCursor().copy(getCurve(iCurve+1).getWidgetCursor());
    plotPanel.invalidateWidgetsAfterCursor(c.getWidgetCursor());
  }
  else { // added at the end, invalidate just this curve
    c.getWidgetCursor().copy(plotPanel.getFirstInvalidWidgetCursor());
  }
  // this line must come AFTER widget cursor lines, or it
  // triggers a full update whenever a curve is added to the end.
  if (getNCurves() > 0)
     maybeSetDefaultBorderColor(c, getNCurves()-1);
}

  /**
   * Removes every curve this chart contains.
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #addCurve(int) addCurve(int)
   * @see #removeCurve removeCurve
   * @see #getNCurves getNCurves
   */
public void clearCurves() {
  while (getNCurves() > 0)
     removeCurve(getNCurves()-1);   
}

  /**
   ** Returns the background color of the chart as a whole.
   **
   ** @return the chart's background color, in a standard
   **   CSS color string format.
   **
   ** @see #setBackgroundColor(String) setBackgroundColor
   **
   **/
  public String getBackgroundColor() {
     return(backgroundColor);
  }
  /**
   ** Returns the color of the border around the chart as
   ** a whole.
   **
   ** @return the color of the chart's border, in a standard
   **   CSS color string format.
   **
   ** @see #setBorderColor(String) setBorderColor
   **
   **/
  public String getBorderColor() {
     return borderColor;
  }

  /**
   ** Returns the width of the border around the chart as a whole
   ** 
   ** @return width of the border around the chart as a whole, as
   **   a CSS border width specification string (e.g. "1px").
   **
   ** @see #setBorderWidth(String) setBorderWidth
   **
   **/
  public String getBorderWidth() {
     return borderWidth;
  }
  /**
   ** Returns the style of the border around the chart as a whole
   ** 
   ** @return cssStyle for the border around the chart as a whole
   **
   ** @see #setBorderStyle(String) setBorderStyle
   **
   **/
  public String getBorderStyle() {
     return borderStyle;
  }

 
     /** Returns the previously specified chart footnotes widget
     *
     * @return widget representing chart's footnotes or null if none.
     *
     * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
     * @see #setChartFootnotes(String) setChartFootnotes(String)
     * @see #getChartTitle getChartTitle
     */
    public Widget getChartFootnotes() {
         return chartFootnotes;
     }
    /** Returns flag indicating if this chart's footnotes are
     *  left-justified or centered.
     *
     * @return true if footnotes are flush against the left edge
     * of the chart, false if they are horizontally centered
     * across the bottom edge of the chart.
     *
     * @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
     * @see #setChartFootnotes(String) setChartFootnotes(String)
     * @see #setChartTitle setChartTitle
     */
    public boolean getChartFootnotesLeftJustified() {
         return chartFootnotesLeftJustified;
     }
    /** Returns the thickness (height) of the rectangular region
     ** at the bottom of the chart allocated for footnotes.
     ** <p>
     **
     ** The width of this region always equals the width of
     ** the entire GChart (including legend and axis labels).
     ** <p>
     **
     ** Your footnotes widget is always vertically centered
     ** in this region.
     ** <p>
     **
     **
     ** Your footnotes widget will either be horizontally
     ** centered in this region, or left justified in it,
     ** depending on the property defined by the
     ** <tt>setChartFootnotesLeftJustified</tt> method.
     **
     ** <p>
     **
     **
     ** This method always returns 0 if the footnotes widget
     ** is <tt>null</tt> (the default); the rectangular
     ** footnotes region is entirely eliminated in that case.
     ** <p>
     **
     ** @return the thickness (height) of the rectangular region
     ** at the bottom of the chart allocated for footnotes, in
     ** pixels.
     **
     ** @see #setChartFootnotesThickness(int) setChartFootnotesThickness
     ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
     **/
     public int getChartFootnotesThickness() {
        int result = 0;
        final int EXTRA_HEIGHT = 3; // 1.5 lines padding above/below
        final int DEF_HEIGHT = 1;
        if (null == getChartFootnotes())
           result = 0;
        else if (GChart.NAI != footnotesThickness)
           result = footnotesThickness;
        else if (getChartFootnotes() instanceof HasHTML)
           result = DEFAULT_FOOTNOTES_THICKNESS * (EXTRA_HEIGHT +
             htmlHeight(((HasHTML) (getChartFootnotes())).getHTML()));
        else
           result = DEFAULT_FOOTNOTES_THICKNESS*
                    (DEF_HEIGHT + EXTRA_HEIGHT);
        return result;
     }
  /** Returns the previously specified widget representing the
   *  chart's title.
   *
   * @return widget representing chart's title or null of no title
   *
   * @see #setChartTitle(Widget) setChartTitle(Widget)
   * @see #setChartTitle(String) setChartTitle(String)
   *
   */
    public Widget getChartTitle() {
       return chartTitle;
    }

     /**
      ** Returns the thickness (height) of the rectangular region at
      ** the top of the chart allocated for the title.
      ** <p>
      **
      ** This method always returns 0 if the title widget
      ** is <tt>null</tt> (the default); the rectangular
      ** title region is entirely eliminated in that case.
      ** <p>
      **
      ** Your title widget is always centered vertically and
      ** horizontally within this rectangular region.
      **
      **
      ** @return the thickness (height) of the rectangle
      ** that contains the chart's title, in pixels.
      **
      ** @see #setChartTitleThickness setChartTitleThickness
      **
      **/
     public int getChartTitleThickness() {
        int result = 0;
        final int EXTRA_HEIGHT = 3; // 1.5 lines above & below title
        final int DEF_HEIGHT = 1;
        if (null == getChartTitle())
           result = 0;
        else if (GChart.NAI != titleThickness)
           result = titleThickness;
        else if (getChartTitle() instanceof HasHTML)
           result = DEFAULT_TITLE_THICKNESS * (EXTRA_HEIGHT +
              htmlHeight(((HasHTML) (getChartTitle())).getHTML()));
        else
           result = DEFAULT_TITLE_THICKNESS*
                    (EXTRA_HEIGHT + DEF_HEIGHT);
        return result;
      }

  /** Convenience method equivalent to <tt>getCurve(getNCurves()-1)</tt>.
   *  <p>
   *  This method, when used in conjunction with no-arg <tt>addCurve</tt>,
   *  method, makes code blocks that create and define the
   *  properties of a chart's curves more readable/editable. For example:
      <pre>
      addCurve(); // add 1st curve
      getCurve().setYAxis(Y2_AXIS); // first setting for 1st curve
      //... other settings for first curve
      addCurve(); // add 2nd curve
      getCurve().setYAxis(Y_AXIS); // first setting for 2nd curve
      // ... other settings for 2nd curve   
      </pre>
   *<p>
   * Note that using the no-arg methods in this way allows you to copy
   * entire groups of curve properties, unchanged, between such curve
   * related blocks.
   *
   *  @return the curve with the highest integer index. In other words,
   *    the curve with an index of <tt>getNCurves()-1</tt>.
   *
   *  @see #getCurve(int) getCurve(int)
   *  @see #getNCurves getNCurves
   *  @see #addCurve() addCurve()
   */
  public Curve getCurve() {
     Curve result = getCurve(getNCurves()-1);
     return result;
  }

     /**
     * Returns a reference to the curve at the specified
     * positional index.  Use the reference returned by this method to
     * modify properties of a curve (the symbol, data points, etc.)
     *
     * <p>
     * @param iCurve index of the curve to be retrieved.
     * @return reference to the Curve at the specified position.
     *
     * @see #getCurve() getCurve()
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #removeCurve removeCurve
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
     public Curve getCurve(int iCurve) {
        if (iCurve < 0 && !systemCurvesAccessible)
           throw new IllegalArgumentException(
             "iCurve = " + iCurve +"; iCurve may not be negative.");
                                             
       Curve result = (Curve) curves.get(iCurve + N_SYSTEM_CURVES);
       return result;
     }

     /**
     * Returns the positional index (within this chart's list of
     * curves) of the specified curve.
     * <p>
     *
     * Returns -1 if the specified curve is not found on
     * this GChart's curve list.
     *
     * <p>
     * @param curve whose list position is to be retrieved
     * @return position of curve in GChart's curve list, or -1
     *        if not on this chart's curve list.
     *
     * @see #getCurve() getCurve()
     * @see #getCurve(int) getCurve(int)
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #removeCurve removeCurve
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
     public int getCurveIndex(Curve curve) {
       int result = curves.indexOf(curve) - N_SYSTEM_CURVES;
       return result;
     }

    /** Returns the font-family used in tick labels, point annotations,
     ** legends, and as the default in titles, footnotes, and
     ** axis labels.
     **
     ** @see #setFontFamily(String) setFontFamily
     **
     **
     **/
     public String getFontFamily() {
       return fontFamily;
     }

    /**
     ** Returns CSS color specification for all gridlines, axes,
     **   and tickmarks.
     **
     ** @see #setGridColor setGridColor
     **
     ** @return the color, in CSS standard color format,
     **    used for all gridlines, axes, and tick marks.
     **
     **/

     public String getGridColor() {
        return gridColor;
     }

    
  /**
   ** Returns the background color of the chart's legend.
   **
   ** @return the legend's background color, in a standard
   **   CSS color string format.
   **
   ** @see #setLegendBackgroundColor setLegendBackgroundColor
   **
   **/
  public String getLegendBackgroundColor() {
     return legendBackgroundColor;
  }
  /**
   ** Returns the border color of the chart's legend.
   **
   ** @return the color of the legend's border, in a standard
   **   CSS color string format.
   **
   ** @see #setLegendBorderColor setLegendBordergroundColor
   **
   **/
  public String getLegendBorderColor() {
     return legendBorderColor;
  }

  /**
   ** Returns the width of the chart's legend's border
   ** 
   ** @return width of the legend's border, in pixels
   **
   ** @see #setLegendBorderWidth setLegendBorderWidth
   **
   **/
  public int getLegendBorderWidth() {
     return legendBorderWidth;
  }

  /**
   ** Returns the style of the chart's legend's border
   ** 
   ** @return cssStyle of the legend's border
   **
   ** @see #setLegendBorderStyle setLegendBorderStyle
   **
   **/
  public String getLegendBorderStyle() {
     return legendBorderStyle;
  }
 
     /**
      ** Returns the color of the font used to display the labels
      **   within the legend (chart key)
      **  
      ** @return CSS color string defining the legend text's color
      **
      ** @see #setLegendFontColor setLegendFontColor
      **/
     public String getLegendFontColor() {
        return legendFontColor;
     }
  /**
   * Returns the font size, in pixels, of text displayed
   * in the chart's legend (also know as a chart's key).
   *
   * @return the (previously specified) font size of legend text
   *
   * @see #setLegendFontSize setLegendFontSize
   */
  public int getLegendFontSize() {
     return legendFontSize;
  }
     /**
      ** Returns the font-style in which this GChart's legend text
      ** will be rendered.
      **
      ** @return font-style of legend text (italic, normal, etc.)
      **
      ** @see #setLegendFontStyle setLegendFontStyle
      **/
     public String getLegendFontStyle() {
        return legendFontStyle;
     }
     /**
      ** Returns true if legend text will be rendered in a bold,
      ** or false if in normal, weight font.
      **
      ** @return if the legend's text is in bold or not.
      **
      ** @see #setLegendFontWeight setLegendFontWeight
      **/
     public String getLegendFontWeight() {
        return legendFontWeight;
     }
     /**
      ** Returns the thickness (width) of the rectangular region
      ** to the right of the y2-axis label allocated for the
      ** chart legend.<p>
      **
      ** The region extends vertically in parallel with the
      ** right edge of the plot area. The legend is always
      ** centered vertically and horizontally within this
      ** rectangular region.
      ** <p>
      **
      ** This method always returns 0 if the legend is not
      ** visible; the rectangular legend region is entirely
      ** eliminated in that case.
      **
      ** @return thickness (width) of legend key holding region,
      ** in pixels.
      **
      ** @see #setLegendThickness setLegendThickness
      **/
     public int getLegendThickness() {
        int result = 0;
        if (isLegendVisible() &&
                0 < getNVisibleCurvesOnLegend()) {
           if (GChart.NAI == legendThickness)
              result = getDefaultLegendThickness();
           else
              result = legendThickness;
        }
       
        return result;

     }
  /**
   * Returns the number of curves on this chart.
   *
   * @return the number of curves on this chart
   *
   * @see #getCurve getCurve
   * @see #addCurve() addCurve()
   * @see #addCurve(int) addCurve(int)
   * @see #removeCurve removeCurve
   * @see #clearCurves clearCurves
   */
  public int getNCurves() {
     return curves.size() - N_SYSTEM_CURVES;
  }
  /** Returns the CSS string that specifies the width of the
   ** padding between the chart and it's external border
   ** <p>
   **
   ** @return the CSS string that defines the CSS padding property
   **   for the GChart as a whole.
   **
   ** @see #setPadding(String) setPadding
   **
   **/
  public String getPadding() {
    return padding;
  }
 
     /**
      ** Returns the background color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      ** @return CSS color string defining the plot area's background
      **    color
      **
      ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
      **/
     public String getPlotAreaBackgroundColor() {
        return plotAreaBackgroundColor;
     }

     /**
      ** Returns the border color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      ** @return CSS color string defining the color of the plot
      **    area's border
      **
      ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
      **/
     public String getPlotAreaBorderColor() {
        return plotAreaBorderColor;
     }
     /**
      ** Returns the width of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      ** @return width, in pixels, of the border around the plot area
      **
      ** @see #setPlotAreaBorderWidth setPlotAreaBorderWidth
      **/
     public int getPlotAreaBorderWidth() {
        return plotAreaBorderWidth;
     }

     /**
      ** Returns the style of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed (the so-called plot area).
      **  
      ** @return CSS style of the border around the plot area
      **
      ** @see #setPlotAreaBorderStyle setPlotAreaBorderStyle
      **/
     public String getPlotAreaBorderStyle() {
        return plotAreaBorderStyle;
     }
     /**
      *
      * Returns a flag that tells if GChart is configured to
      * perform updates so that the chart uses less memory.
      *
      * @return <tt>true</tt> if GChart optimizes updates to
      * save memory, <tt>false</tt> (the default) if it optimizes
      * them to save time.
      *
      * @see #setOptimizeForMemory setOptimizeForMemory
      *
      **/
     public boolean getOptimizeForMemory() {
        return optimizeForMemory;
     }
    
    /**
     * Determinines if this chart will display points whose x,y locations
     * are outside of the axis limits associated with the chart.
     *
     * @return true if off-the-chart points are displayed, false
     *    otherwise.
     *
     * @see #setShowOffChartPoints setShowOffChartPoints
     * @see #setShowOffDecoratedChartGlyphs
     *  setShowOffDecoratedChartGlyphs
     */
   
    public boolean getShowOffChartPoints() {
       return showOffChartPoints;
    }

    /**
     * Determinines if this chart will clip any chart elements
     * that extend beyond the bounds of the decorated chart.
     * The decorated chart includes title, footnotes, etc.
     * as well as the plot area proper.
     *
     * @return true if off-the-decorated-chart elements are
     * clipped, false otherwise.
     *
     * @see #setShowOffDecoratedChartGlyphs
     *   setShowOffDecoratedChartGlyphs
     * @see #setShowOffChartPoints setShowOffChartPoints
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     *
     */
    public boolean getShowOffDecoratedChartGlyphs() {
       return showOffDecoratedChartGlyphs;
    }

    /**
   ** Returns a URL that points to a 1 x 1 pixel blank image
   ** file GChart requires to render its charts without
   ** producing missing image icons.
   **
   ** <p>
   **
   ** @return the URL of the file GChart needs to prevent
   ** missing image icons from appearing on your chart.
   **
   ** @see #setBlankImageURL setBlankImageURL
   **
   **/

     public static String getBlankImageURL() {
        return null == blankImageURL ? DEFAULT_BLANK_IMAGE_URL :
              blankImageURL;
     }

    /**
     * Returns the x-axis associated with this chart. Use the
     * returned reference to manipulate axis min and max,
     * number of ticks, tick positions, tick label formats, etc.
     * <p>
     * @return object representing the x-axis of this chart.
     *
     * @see #getYAxis getYAxis
     * @see #getY2Axis getY2Axis
     */
    public Axis getXAxis() {
       return xAxis;
    }

    /**
     * Returns the number of x-pixels in the region of the chart
     * used for curve display purposes.
     *
     * @return the number of x-pixels available for curve display.
     *
     * @see #setXChartSize setXChartSize
     *
     */
    public int getXChartSize() {
       return xChartSize;
    }

    /**
     * Returns the number of x-pixels reserved for the chart as a
     * whole, including space reserved for decorations (title,
     * footnotes, axis labels, ticks, tick labels, legend key,
     * etc.).
     * <p>
     *
     * The returned size does not include the border or padding
     * around the chart as a whole. <p>
     *
     * You cannot directly set the decorated x chart size.
     * Instead, you must set the width of the plot area, and the
     * thicknesses of certain of the decoration-holding regions
     * (using methods linked to below) that, summed together,
     * define the total width of the chart.
     *
     * @return the width of the entire chart, in pixels.
     *
     * @see #setXChartSize setXChartSize
     * @see YAxis#setAxisLabelThickness YAxis.setAxisLabelThickness
     * @see YAxis#setTickLabelThickness YAxis.setTickLabelThickness
     * @see YAxis#setTickLength YAxis.setTickLength
     * @see Y2Axis#setAxisLabelThickness Y2Axis.setAxisLabelThickness
     * @see Y2Axis#setTickLabelThickness Y2Axis.setTickLabelThickness
     * @see Y2Axis#setTickLength Y2Axis.setTickLength
     * @see #setLegendThickness setLegendThickness
     *
     */
    public int getXChartSizeDecorated() {
       int result = getXChartSize() +
                    getYAxis().getAxisLabelThickness() +
                    getYAxis().getTickLabelThickness() +
                    getYAxis().getTickLength() +
                    getY2Axis().getAxisLabelThickness() +
                    getY2Axis().getTickLabelThickness() +
                    getY2Axis().getTickLength() +
                    getLegendThickness();
       return result;
    }

   
    /**
     * Returns the y2-axis (right y axis) associated with this
     * chart. Use the returned reference to manipulate axis
     * min and max, number of ticks, tick positions, tick
     * label formats, etc.
     *
     * <p>
     * @return object representing the y2-axis of this chart.
     *
     * @see #getYAxis getYAxis
     * @see #getXAxis getXAxis
     */
    public Axis getY2Axis() {
       return y2Axis;
    }
    /**
     * Returns the (left) y-axis associated with this chart. Use the
     * returned reference to manipulate axis min and max,
     * number of ticks, tick positions, tick label formats, etc.
     * <p>
     * @return object representing the y-axis of this chart.
     *
     * @see #getXAxis getXAxis
     * @see #getY2Axis getY2Axis
     */
    public Axis getYAxis() {
       return yAxis;
    }
    /**
     * Returns the number of y-pixels in the region of the chart
     * used for curve display purposes.
     *
     * @return the number of y-pixels available for curve display.
     *
     * @see #setYChartSize setYChartSize
     *
     */
    public int getYChartSize() {
       return yChartSize;
    }

    /**
     * Returns the number of y-pixels reserved for the chart as a
     * whole, including space reserved for decorations (title,
     * footnotes, axis labels, ticks, tick labels, etc.).  <p>
     *
     * The returned size does not include the border or padding
     * around the chart as a whole. <p>
     *
     * You cannot directly set the decorated y chart size.
     * Instead, you must set sizes and thicknesses of the
     * plot area and certain of the decoration-holding regions
     * (using the methods linked-to below) that, when summed
     * together, define the height of the decorated chart.
     *
     * @return the height of the entire chart, in pixels.
     *
     * @see #setYChartSize setYChartSize
     * @see XAxis#setAxisLabelThickness YAxis.setAxisLabelThickness
     * @see XAxis#setTickLabelThickness YAxis.setTickLabelThickness
     * @see XAxis#setTickLength YAxis.setTickLength
     * @see #setChartTitleThickness setChartTitleThickness
     * @see #setChartFootnotesThickness setChartFootnotesThickness
     *
     */
    public int getYChartSizeDecorated() {
       int result = getYChartSize() +
                    getXAxis().getAxisLabelThickness() +
                    getXAxis().getTickLabelThickness() +
                    getXAxis().getTickLength() +
                    getChartTitleThickness() +
                    getChartFootnotesThickness();
      
       return result;
    }



   
     /**
     * Determines if this chart has a "y2" (right) y-axis.
     * <p>
     * Only charts that have at least one curve on the right
     * y axis will have a y2-axis.
     *
     * @return true if the chart has a second y axis, false otherwise.
     *
     * @see Curve#setYAxis Curve.setYAxis
     */
    public boolean hasY2Axis() {
       boolean result = false;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (c.isVisible() && c.onY2()) {
             result = true;
             break;
          }
       }
       return result;
    }
    /**
     * Determines if this chart has an ordinary, or left, y-axis.
     * <p>
     * Only charts that have at least one curve on the left
     * y axis will have a y-axis.
     *
     * @return true if the chart has a left y axis, false otherwise
     *
     * @see Curve#setYAxis Curve.setYAxis
     *
     */
    public boolean hasYAxis() {
       boolean result = false;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (c.isVisible() && !c.onY2()) {
             result = true;
             break;
          }
       }
       return result;
    }
    /**
     * Determines if the legend of this chart is visible.
     *
     *
     * @return true if the legend is visible, false otherwise.
     *
     * @see #setLegendVisible setLegendVisible
     */
    public boolean isLegendVisible() {return isLegendVisible;}
   
   /**
     * Removes the curve at the specified positional index.
     * <p>
     * @param iCurve index of the curve to be removed
     *
     * @see #getCurve getCurve
     * @see #addCurve() addCurve()
     * @see #addCurve(int) addCurve(int)
     * @see #clearCurves clearCurves
     * @see #getNCurves getNCurves
     */
public void removeCurve(int iCurve) {
     if (iCurve < 0 && !systemCurvesAccessible)
       throw new IllegalArgumentException(
         "iCurve = " + iCurve +"; iCurve may not be negative.");
                                             
     plotPanel.invalidateWidgetsAfterCursor(
        getCurve(iCurve).getWidgetCursor());
     curves.remove(iCurve+N_SYSTEM_CURVES);
}

  /**
   ** Specifies the background color of the chart as a whole.
   **
   ** <p>
   ** The default background color is <tt>USE_CSS</tt>.
   ** <p>
   **
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the chart's background color, in a standard
   **   CSS color string format.
   **
   **
   ** @see #USE_CSS USE_CSS
   **
   **/
  public void setBackgroundColor(String cssColor) {
     chartDecorationsChanged = true;
     backgroundColor = cssColor;
  }

    /**
     ** Specifies a URL that points to the transparent, 1 x 1 pixel,
     ** "blank GIF" that GChart uses in order to render your
     ** chart without adding spurious "missing image" icons to it.
     ** <p>
     **
     ** When GWT compiles an application that imports the GChart
     ** library, it automatically adds an appropriate blank
     ** image, <tt>gchart.gif</tt>, to the module base directory
     ** (this is the directory into which GWT also copies your
     ** compiled Javascript, all the files in your public
     ** directory, etc.).  <p>
     **
     ** By default, GChart uses
     ** <tt>"gchart.gif"</tt> as the <tt>blankImageURL</tt>.
     ** Because for most GWT applications (e.g. Hello World)
     ** the module base folder is also the host page's base
     ** folder, this default (relative) URL usually automatically
     ** references an appropriate blank image file, and no missing
     ** image icons will appear on your charts.
     **
     ** <p>
     **
     ** However, for some GWT applications, the host page base
     ** directory is not the same as the module base directory.
     ** For example, you may have decided to relocate the host
     ** page into a different directory than where GWT's compiler
     ** placed it, or you may be accessing the GWT compiled
     ** Javascript implementing your chart from a host page on a
     ** completely different web site, or perhaps you simply have
     ** employed the HTML <tt>base</tt> tag to change the host
     ** page's base directory to one different from the module
     ** base directory.  <p>
     **
     ** For such "non-standard" configurations, you will get
     ** missing image icons in your charts unless you explicitly
     ** tell GChart where it can find the built-in (or some
     ** equivalent) blank image.  The easiest (but not always the
     ** most memory efficient) way to do this is to add the
     ** following line to your application's initialization code:
     **
     ** <p>
     ** <pre>
     **   GChart.setBlankImageURL(
     **     GWT.getModuleBaseURL()+GChart.DEFAULT_BLANK_IMAGE_URL);
     ** </pre>
     ** <p>
     **
     ** <small>I got the above technique from <a
     ** href="http://groups.google.com/group/Google-Web-Toolkit/msg/4be3f19dc14f958a">
     ** this GWT forum post by Dean S. Jones</a>, which also
     ** identified the GChart bug this method was added to fix.
     ** </small>
     ** <p>
     **
     **
     ** This code converts the default, short, relative URL
     ** (<tt>"gchart.gif"</tt>) into an equivalent, absolute, URL.
     ** Though it fixes the problem, it adds a potentially very
     ** long URL to every <tt>img</tt> element added by GChart to
     ** render your chart, which can (in theory) more than double
     ** the memory required to represent your chart in the
     ** browser, because the absolute URLs can be of undetermined
     ** length.  In practice, browser memory usage increases of
     ** 10% have been observed with the on-line demo GChart and a
     ** typicial, 60-odd character absolute URL.  <p>
     **
     ** You have several alternatives to the above quick fix that can
     ** often reduce the length of the URL and thus save browser
     ** memory:
     **
     ** <p>
     **
     ** <ol> <li>Simply copy <tt>gchart.gif</tt> from the module
     **   base directory into the host page's base directory, and
     **   continue using the default <tt>"gchart.gif"</tt>
     **   relative URL.
     **
     **   <li>Use the HTML <tt>base</tt> tag to redefine the host
     **       page base directory so that it points to the
     **       module base directory.
     **
     **   <li>If the relative path from the host page base
     **       directory to the module base directory is
     **       reasonably short, pass that alternative
     **       relative URL to this method (note that all
     **       relative URLs are interpreted relative to the base
     **       directory of the host page containing your GChart).
     **
     **   <li>Place a copy of <tt>gchart.gif</tt> into
     **       a directory whose absolute URL is very short,
     **       and then pass that short absolute URL to this method.
     **
     ** </ol>   
     ** <p>
     **
     ** <small> <i>Special note to anyone reading
     ** this who designed HTML's <tt>image</tt> tag:</i> If you
     ** had provided a <tt>src=none</tt> option, this method
     ** would not have to exist.
     ** </small>
     ** <p>
     **
     ** <i>Tip:</i> If you already have an appropriate blank
     ** gif on your site that is accessible from the host
     ** page via a reasonably short URL there is no need to
     ** copy <tt>gchart.gif</tt>. You can just pass that URL
     ** to this method.
     **
     ** <p>
     **
     ** @param blankImageURL a URL that points to a 1 x 1 pixel
     ** transparent image that GChart needs to render your
     ** charts without adding a spurious "missing image" icon.
     **
     ** @see #getBlankImageURL getBlankImageURL
     ** @see #DEFAULT_BLANK_IMAGE_URL DEFAULT_BLANK_IMAGE_URL
     **
     **/

    public static void setBlankImageURL(
       String blankImageURL) {
       if (blankImageURL != GChart.blankImageURL) {
          GChart.blankImageURL = blankImageURL;
// Decided not to prefetch blank image URL because 1) pre-fetching
// doesn't improve performance noticably in tested browsers,
// 2) there are reports of possible memory leaks associated with
// its use in the GWT issue tracker, and 3) users can
// easily do the prefetch on their own if they want to, and that
// is really the right place to do a prefetch anyway.
//          Image.prefetch(GChart.getBlankImageURL());
       }
    }
  /**
   ** Specifies the color of the border around the chart as
   ** a whole.
   **
   ** <p>
   ** The default border color is <tt>USE_CSS</tt>.
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   ** <p>
   **
   **
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **
   ** @param cssColor the color of the chart's border, in a standard
   **   CSS color string format.
   **
   ** @see #setBorderWidth(String) setBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #getBorderColor getBorderColor
   ** @see #USE_CSS USE_CSS
   **
   **/
  public void setBorderColor(String cssColor) {
     chartDecorationsChanged = true;
     borderColor = cssColor;
  }

  /**
   ** Specifies the width of the border around the chart as a whole.
   ** 
   ** <p>
   ** The default border width is <tt>USE_CSS</tt>.
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   **
   ** @param cssWidth width of the border around the chart as a whole,
   **   expressed as a CSS border-width specification string, such
   **   as "1px".
   **
   ** @see #getBorderWidth getBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #setBorderColor(String) setBorderColor
   ** @see #USE_CSS USE_CSS
   **/
  public void setBorderWidth(String cssWidth) {
     chartDecorationsChanged = true;
     borderWidth = cssWidth;
  }
  /**
   ** Sets style of the border around the chart as a whole.
   **
   ** <p>
   ** The default border style is <tt>USE_CSS</tt>.
   ** <p>
   **
   ** <p>
   ** <blockquote><small>
   ** <i>Tip:</i> No border will appear if either <tt>borderStyle</tt>
   ** is <tt>none</tt>, <tt>borderWidth</tt> is <tt>0px</tt> or
   ** <tt>borderColor</tt> is <tt>transparent</tt>. Since
   ** these will often be the "CSS inherited" values,
   ** generally, it's best to set all three properties
   ** whenever you set any one of them.
   ** </small></blockquote>
   ** <p>
   **
   ** 
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getBorderStyle getBorderStyle
   ** @see #setBackgroundColor(String) setBackgroundColor
   ** @see #setBorderColor(String) setBorderColor
   ** @see #setBorderWidth(String) setBorderWidth
   ** @see #USE_CSS USE_CSS
   **
   **
   **/
     public void setBorderStyle(String borderStyle) {
        chartDecorationsChanged = true;
        this.borderStyle = borderStyle;
     }

 
    /**
      * Convenience method equivalent to
      * <tt>setChartFootnotes(new HTML(html))</tt>.
      *
      * @param html HTML text used to define the chart's title.
      *
      * @see #setChartFootnotes(Widget) setChartFootnotes(Widget)
      */
  public void setChartFootnotes(String html) {
    setChartFootnotes(new HTML(html));
  }
    /** Sets widget that appears just below the chart.
     *  <p>
     *
     *  The widget will vertically centered within a band just
     *  below the x axis label that stretches along the entire
     *  bottom edge of the chart, and whose height is defined by
     *  <tt>setChartFootnotesThickness</tt>.
     *
     *  <p>
    
     *  The widget will either be left justified, or horizontally
     *  centered, within this band depending on the property
     *  defined by <tt>setChartFootnotesLeftJustified</tt>
    
     * 
     *  @param chartFootnotes widget representing the chart's footnotes
     *
     *  @see #setChartFootnotes(String) setChartFootnotes(String)
     *  @see #setChartFootnotesThickness setChartFootnotesThickness
     *  @see #getChartFootnotes getChartFootnotes
     *  @see #setChartFootnotesLeftJustified
     *  setChartFootnotesLeftJustified
     */
    public void setChartFootnotes(Widget chartFootnotes) {
       chartDecorationsChanged = true;
       this.chartFootnotes = chartFootnotes;
    }
   
    /** Defines if this chart's footnotes are left justified,
     *  or horizontally centered across the bottom edge of the
     *  chart.
     *  <p>
     *  Note that a chart's footnotes are always vertically
     *  centered within the band at the bottom of the chart
     *  reserved for chart footnotes. Use the
     *  <tt>setChartFootnotesThickness</tt> method to set the
     *  height of this band.
     *
     *  @param footnotesLeftJustified true to position chart
     *  footnotes flush against the left edge of the chart, false
     *  to center them horizontally across the chart's bottom
     *  edge.
     *
     *  @see #setChartFootnotes(String) setChartFootnotes(String)
     *  @see #getChartFootnotes getChartFootnotes
     *  @see #setChartFootnotesThickness
     */
    public void setChartFootnotesLeftJustified(boolean footnotesLeftJustified) {
       chartDecorationsChanged = true;
       chartFootnotesLeftJustified = footnotesLeftJustified;
    }

     /**
      ** Sets the thickness (height) of the rectangular region at
      ** the bottom of the chart allocated for the footnotes.
      ** <p>
      **
      ** The width of this region always equals the width of
      ** the entire GChart (including legend and axis labels).
      ** <p>
      **
      ** Your footnotes widget is always vertically centered
      ** in this region.
      ** <p>
      **
      **
      ** Your footnotes widget will either be horizontally
      ** centered in this region, or left justified in it,
      ** depending on the property defined by the
      ** <tt>setChartFootnotesLeftJustified</tt> method.
      ** <p>
      **
      ** This setting has no impact on chart layout if the
      ** footnotes widget is <tt>null</tt> (the default); the
      ** rectangular footnotes region is entirely eliminated, and
      ** in effect has a 0 thickness, in that case.
      ** <p>
      **
      ** If you set the footnotes thickness to <tt>GChart.NAI</tt>
      ** (the default) GChart will use a thickness based on
      ** the estimated number of (<tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt>
      ** delimited) lines.
      **
      ** @param thickness the thickness (height) of the rectangle
      ** that contains the footnotes, in pixels, or
      ** <tt>GChart.NAI</tt> to use the default thickness.
      **
      ** @see #getChartFootnotesThickness getChartFootnotesThickness
      ** @see #setChartFootnotesLeftJustified setChartFootnotesLeftJustified
      ** @see GChart#NAI GChart.NAI
      ** @see #DEFAULT_FOOTNOTES_THICKNESS
      ** DEFAULT_FOOTNOTES_THICKNESS
      **
      **/
     public void setChartFootnotesThickness(int thickness) {
        chartDecorationsChanged = true;
        this.footnotesThickness = thickness;
     }

    /**
      * Convenience method equivalent to
      * <tt>setXChartSize(xChartSize); setYChartSize(yChartSize)</tt>.
      *
      * @param xChartSize number of x-pixels in the curve
      *   display area of the chart
      * @param yChartSize number of y-pixels in the curve
      *   display area of the chart
      *
      * @see #setXChartSize setXChartSize
      * @see #setYChartSize setYChartSize
      *
      */
    public void setChartSize(int xChartSize, int yChartSize) {
       setXChartSize(xChartSize);
       setYChartSize(yChartSize);
    }
   
    /**
      * Convenience method equivalent to
      * <tt>setChartTitle(new HTML(html))</tt>.
      *
      * @param html HTML text used to define the chart's title.
      *
      * @see #setChartTitle(Widget) setChartTitle(Widget)
      */
  public void setChartTitle(String html) {
    setChartTitle(new HTML(html));
  }

    // returns x,y min/max over every plotted curve
   
    /**
     * Specifies the widget that appears centered just above the chart.
     *
     * @param chartTitle the widget to be used as this chart's title.
     *
     * @see #setChartTitle(String) setChartTitle(String)
     * @see #setChartTitleThickness setChartTitleThickness
     * @see #getChartTitle getChartTitle
     *
     */
    public void setChartTitle(Widget chartTitle) {
       chartDecorationsChanged = true;
       this.chartTitle = chartTitle;
    }

     /**
      ** Sets the thickness (height) of the rectangular region at
      ** the top of the chart allocated for the title.
      ** <p>
      **
      ** Your title widget is always centered vertically and
      ** horizontally within this rectangular region. <p>
      **
      ** This setting has no impact on chart layout if the title
      ** widget is <tt>null</tt>, since the title-holding
      ** region is entirely eliminated in that case.
      **
      ** If you set the title thickness to <tt>GChart.NAI</tt>
      ** (the default) GChart will use a thickness that is
      ** based on the the number of <tt>&lt;br&gt;</tt> or
      ** <tt>&lt;li&gt;</tt> delimited HTML lines if the title Widget
      ** implements <tt>HasHTML</tt>.
      **
      ** @param thickness the thickness (height) of the rectangle
      ** that contains the title, in pixels, or
      ** <tt>GChart.NAI</tt> to use the default thickness.
      **
      ** @see #getChartTitleThickness getChartTitleThickness
      ** @see GChart#NAI GChart.NAI
      ** @see #DEFAULT_TITLE_THICKNESS
      ** DEFAULT_TITLE_THICKNESS
      **
      **/
     public void setChartTitleThickness(int thickness) {
        chartDecorationsChanged = true;
        this.titleThickness = thickness;
     }

    /** Sets the font-family used in tick labels, point annotations,
     ** legends, titles, footnotes, and
     ** axis labels.
     ** <p>
     ** If not specified, the default value is <tt>USE_CSS</tt>.
     ** <p>
     **
     ** Note that titles, footnotes and axis labels are
     ** defined via externally created Widgets, which are free
     ** to override the font-family specified by this
     ** method.
     **
     ** @param fontFamily a CSS font-family specification, such
     **   as "Arial, sans-serif"
     **
     ** @see #getFontFamily getFontFamily
     ** @see #USE_CSS USE_CSS
     **
     **/
     public void setFontFamily(String fontFamily) {
       chartDecorationsChanged = true;
       this.fontFamily = fontFamily;
     }


   
    /**
     ** Specifies the single color used for all gridlines, axes
     ** lines, and tick marks.
     **
     **
     ** <p>
     ** For more information on standard CSS color
     ** specifications see the discussion in
     ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
     ** <p>
     **       
     ** @param cssColor the color, in CSS standard color format,
     **    to be used for all gridlines, axes, and tick marks.
     **
     ** @see #getGridColor getGridColor
     ** @see #DEFAULT_GRID_COLOR DEFAULT_GRID_COLOR
     **
     **/
    public void setGridColor(String cssColor) {
      plotPanel.invalidateWidgetsAfterCursor(
         plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
      gridColor = cssColor;
    }
  /**
   ** Sets the background color of the chart's legend.
   **
   **
   ** <p>
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the legend's background color, in a standard
   **   CSS color string format.
   **
   ** @see #getLegendBackgroundColor getLegendBackgroundColor
   ** @see #DEFAULT_LEGEND_BACKGROUND_COLOR
   **       DEFAULT_LEGEND_BACKGROUND_COLOR
   **/
  public void setLegendBackgroundColor(String cssColor) {
     chartDecorationsChanged = true;
     legendBackgroundColor = cssColor;
  }
  /**
   ** Sets the border color of the chart's legend.
   **
   **
   ** <p>
   ** For more information on standard CSS color
   ** specifications see the discussion in
   ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
   ** <p>
   **       
   ** @param cssColor the color of the legend's border, in a standard
   **   CSS color string format.
   **
   ** @see #getLegendBorderColor getLegendBorderColor
   ** @see #DEFAULT_LEGEND_BORDER_COLOR DEFAULT_LEGEND_BORDER_COLOR
   **/
  public void setLegendBorderColor(String cssColor) {
     chartDecorationsChanged = true;
     legendBorderColor = cssColor;
  }
  /**
   ** Sets the width of the chart legend's border.
   **
   ** @param width the width of the legend's border, in pixels
   **
   ** @see #getLegendBorderWidth getLegendBorderWidth
   ** @see #DEFAULT_LEGEND_BORDER_WIDTH DEFAULT_LEGEND_BORDER_WIDTH
   **/
  public void setLegendBorderWidth(int width) {
     chartDecorationsChanged = true;
     legendBorderWidth = width;
  }
  /**
   ** Sets style of the border around the chart's legend (key).
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getLegendBorderStyle getLegendBorderStyle
   ** @see #setLegendBackgroundColor setLegendBackgroundColor
   ** @see #setLegendBorderColor setLegendBorderColor
   ** @see #DEFAULT_LEGEND_BORDER_STYLE DEFAULT_LEGEND_BORDER_STYLE
   **/
     public void setLegendBorderStyle(String borderStyle) {
        chartDecorationsChanged = true;
        legendBorderStyle = borderStyle;
     }
     /**
      ** Specifies the color of the legend's font. Default is
      ** <tt>DEFAULT_FONT_COLOR</tt>.
      **
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor color of the font used to display the
      **    labels in the legend.
      **
      ** @see #getLegendFontColor getLegendFontColor
      ** @see #DEFAULT_FONT_COLOR DEFAULT_FONT_COLOR
      **
      **/
     public void setLegendFontColor(String cssColor) {
        chartDecorationsChanged = true;
        legendFontColor = cssColor;
     }

    /**
     * Specifies the font size, in pixels, of text displayed
     * in the chart's legend (also know as a chart's key).
     * <p>
     * This size also governs the size of the symbol icon
     * displayed in the legend.
     * <p>
     * Default is <tt>DEFAULT_LEGEND_FONTSIZE</tt>.
     *
     * @param legendFontSize the font size of legend text
     *
     * @see #getLegendFontSize getLegendFontSize
     * @see #DEFAULT_LEGEND_FONTSIZE DEFAULT_LEGEND_FONTSIZE
     *
     */
    public void setLegendFontSize(int legendFontSize) {
       chartDecorationsChanged = true;
       this.legendFontSize = legendFontSize;
    }
     /**
      ** Specifies the cssStyle of the font used to render the
      ** legend's labels. Default is <tt>DEFAULT_FONT_STYLE</tt>.
      **
      ** @param cssStyle any valid CSS font-style, namely,
      **   normal, italic, oblique, or inherit.
      **
      ** @see #getLegendFontStyle getLegendFontStyle
      ** @see #DEFAULT_FONT_STYLE DEFAULT_FONT_STYLE
      **/
     public void setLegendFontStyle(String cssStyle) {
        chartDecorationsChanged = true;
        legendFontStyle = cssStyle;
     }
   
     /**
      ** Specifies the weight of the font used in the labels of the
      ** legend. Default is <tt>DEFAULT_FONT_WEIGHT</tt>.
      **
      ** @param cssWeight a CSS font-weight specification, such as
      **    bold, bolder, normal, light, 100, 200, ... or 900.
      **
      ** @see #getLegendFontWeight getLegendFontWeight
      ** @see #DEFAULT_FONT_WEIGHT DEFAULT_FONT_WEIGHT
      **/
     public void setLegendFontWeight(String cssWeight) {
        chartDecorationsChanged = true;
        legendFontWeight = cssWeight;
     }
     /**
      ** Sets the thickness (width) of the rectangular region at
      ** the right of the chart allocated for the legend key.
      ** <p>
      **
      ** This setting has no impact on chart layout if the
      ** legend key is not visible, since the legend key's
      ** rectangular region is entirely eliminated in that
      ** case.
      **
      ** <p>
      **
      ** If the legend thickness is set to <tt>GChart.NAI</tt>
      ** (the default) GChart uses an heuristic to set the legend
      ** thickness based on the number of characters in each
      ** curve's legend label.
      **
      **
      ** @param legendThickness the thickness (width) of the rectangle
      ** that contains the legend key, in pixels, or
      ** <tt>GChart.NAI</tt> to use a built-in heurstic
      ** to determine the legend width.
      **
      ** @see #getLegendThickness getLegendThickness
      ** @see Curve#setLegendLabel setLegendLabel
      ** @see Y2Axis#setAxisLabelThickness Y2Axis.setAxisLabelThickness
      **
      **/
     public void setLegendThickness(int legendThickness) {
        chartDecorationsChanged = true;
        this.legendThickness = legendThickness;
     }
    
    /**
     * Specifies if the legend is to be visible on this chart.
     * Legends are visible by default. However, a legend is only
     * generated if at least one curve's legend label has been
     * specified.
     *
     * @param isLegendVisible true to display the legend, false to
     * hide it.
     *
     * @see #isLegendVisible isLegendVisible
     * @see Curve#setLegendLabel setLegendLabel
     */
    public void setLegendVisible(boolean isLegendVisible) {
       chartDecorationsChanged = true;
       this.isLegendVisible = isLegendVisible;
    }

    /**
     * By default, this property is <tt>false</tt>, which means
     * that GChart will retain no-longer-needed Image and Grid
     * widgets (plus any user object references associated with
     * those widgets, such as those created via the
     * <tt>setAnnotationText</tt> and
     * <tt>setAnnotationWidget</tt> methods) between
     * <tt>updates</tt> in the expectation that they may be
     * needed by future updates.  This strategy often makes
     * updates faster, because building new Image and Grid
     * elements "from scratch" is very expensive.  However,
     * strictly speaking, GChart is holding onto memory it no
     * longer needs to render the chart <i>right now</i>--which
     * would normally be considered a memory leak if it were not
     * being done deliberately.  <p>
     *
     * If <tt>optimizeForMemory</tt> is set to <tt>true</tt>,
     * GChart will (at the very next <tt>update()</tt> call) free
     * up any Image or Grid elements that are no longer required
     * to render the current chart.  Should a chart's size grow back
     * to a former size, the subsequent update would be slower,
     * though.
     *
     * <p> Charts that use exactly the same number of Image and
     * Grid elements for each update (for example a bar chart
     * where the number of bars is fixed) will see no impact on
     * either memory use or update speeds by setting this
     * parameter.  Charts that have a highly variable number of
     * Image or Grid elements (for example, a chart whose number
     * of points varies randomly between 5 and 500) may see a
     * very large impact on speed (false is faster) or memory
     * (true is more compact).
     * <p>
     *
     * The setting of this parameter never has any impact on the
     * speed or memory used on the <i>very first</i> chart
     * update.
     * <p>
     *
     * In one test using the future oil price simulation chart of
     * GChart's live demo (which has only small changes in the
     * number of elements required to render the chart between
     * updates) setting this parameter to true made the updates,
     * on average, around 10% slower, but also reduced the memory
     * footprint by around 2%.
     *
     * @param optimizeForMemory <tt>true</tt> to optimize updates
     * to use less memory, <tt>false</tt> (the default) to
     * optimize them to use less time.
     *
     * @see #update update
     *
     */
    public void setOptimizeForMemory(boolean optimizeForMemory) {
       this.optimizeForMemory = optimizeForMemory;
    }
  /**
   ** Specifies the amount of padding to add just inside of the
   ** chart's border, as a CSS padding specification string.
   ** <p>
   ** 
   ** <p>
   ** The default padding is <tt>USE_CSS</tt>.
   **
   ** <p>
   **
   ** @param cssPadding the width of the padding, as a CSS padding
   **   specification string
   **   (e.g. use "1px" to introduce a 1 pixel padding
   **   just between the chart' border and the chart itself)
   **
   ** @see #getPadding getPadding
   ** @see #setBorderWidth setBorderWidth
   ** @see #setBorderStyle(String) setBorderStyle
   ** @see #setBorderColor(String) setBorderColor
   ** @see #USE_CSS USE_CSS
   **/
  public void setPadding(String cssPadding) {
     chartDecorationsChanged = true;
     padding = cssPadding;
  }

     /**
      ** Specifies the background color of the area of the chart
      ** in which symbols representing curve data are displayed
      **  
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor CSS color string defining the plot
      **    area's background color
      **
      ** @see #getPlotAreaBackgroundColor getPlotAreaBackgroundColor
      **/
     public void setPlotAreaBackgroundColor(String cssColor) {
        plotAreaBackgroundColor = cssColor;
        plotPanel.plotAreaNeedsUpdate();
     }

     /**
      ** Specifies the color of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      **
      ** <p>
      ** For more information on standard CSS color
      ** specifications see the discussion in
      ** {@link Symbol#setBackgroundColor Symbol.setBackgroundColor}.
      ** <p>
      **       
      ** @param cssColor CSS color string defining the color of
      **    the plot area's border
      **
      ** @see #getPlotAreaBorderColor getPlotAreaBorderColor
      **/
     public void setPlotAreaBorderColor(String cssColor) {
        plotAreaBorderColor = cssColor;
        plotPanel.plotAreaNeedsUpdate();
     }
     /**
      ** Specifies the width of the border around the area of the
      ** chart in which symbols representing curve data are
      ** displayed.
      **  
      ** @param width the width, in pixels, of the border around
      **   the plot area
      **
      ** @see #getPlotAreaBorderWidth getPlotAreaBorderWidth
      **/
     public void setPlotAreaBorderWidth(int width) {
        plotAreaBorderWidth = width;
        plotPanel.plotAreaNeedsUpdate();
     }
  /**
   ** Sets style of the border around the chart's plot area
   ** (the rectangular area where the curves are drawn).
   **
   ** <p>
   **
   ** <p>
   ** @param borderStyle a CSS border style such as
   ** "solid", "dotted", "dashed", etc.
   **
   ** @see #getPlotAreaBorderStyle getPlotAreaBorderStyle
   ** @see #setPlotAreaBackgroundColor setPlotAreaBackgroundColor
   ** @see #setPlotAreaBorderColor setPlotAreaBorderColor
   **/
     public void setPlotAreaBorderStyle(String borderStyle) {
        this.plotAreaBorderStyle = borderStyle;
        plotPanel.plotAreaNeedsUpdate();
     }

     /* Sets the image URL that defines the background of
      * the GChart plot area. The GChart plot area is the
      * rectangular region defined by the x and y axes of
      * the plot, but does not include those axes (or
      * their ticks).
      * <p>
      * Note that by default, or if this URL is set to null,
      * GChart will use the URL returned by
      * <tt>getBlankImageURL</tt>.
      *
      * <small><b>Ideas/tips for using the plot area background
      * URL:</b>
      * <blockquote>
      * <ol>
      *  <li> It's often best to
      *  exactly match the width and height of the image
      *  with the GChart plot area width and height
      *  (defined via (via <tt>setChartSize</tt>). Otherwise,
      *  the image will be scaled up or down to fit the
      *  plot area, which usually doesn't look that great.
      *
      *  <li>Note that since a Google Chart API url is just
      *  an image url to GChart, you can easily use a
      *  Google Chart API url to define the background of an
      *  otherwise client-side chart. For example, you
      *  might place a static 3-D pie chart behind
      *  a rapidly changing client-side GChart bar chart.
      * 
      * <li> Note that this method's image will appear <i>behind</i>
      * every gridline and curve on the chart.  To overlay
      * images <i>on top of</t> the gridlines or other curves, or
      * even to place them outside of the plot area, use a
      * dedicated curve and its symbol's <tt>setImageURL</tt>
      * method, or simply embed such images within HTML-defined
      * point annotations.
      * </ol>
      *
      * </blockquote></small>
      *
      * @see #getPlotAreaImageURL getPlotAreaImageURL
      * @see #setBlankImageURL setBlankImageURL
      * @see GChart.Symbol#setImageURL setImageURL
      *
      * @param imageURL URL of the image used as the background
      * of the plot area. 
      *
      */
    
     public void setPlotAreaImageURL(String imageURL) {
        plotPanel.plotAreaNeedsUpdate();
        plotPanel.setPlotAreaImageURL(imageURL);
     }

     /*
      * Returns the image URL that will be used to define the
      * plot area's background the next time <tt>update</tt> is called.
      * <p>
      *
      * @return url of image to be used as the background of the plot
      * area the next time that <tt>update</tt> is called.
      *
      * @see #setPlotAreaImageURL setPlotAreaImageURL
      * @see update update
      *
      */
     public String getPlotAreaImageURL() {
        String result = plotPanel.getPlotAreaImageURL();
        return result;
     }
    /** Specifies if points falling outside the ranges defined by
     *  the chart's axis limits are to be visible or hidden.
     *  <p>
     *  Note that any off-the-plot-area clipping is "all or nothing".
     *  That is, if a point's x,y location is within range,
     *  the entire point symbol and any annotation will also be visible.
     *  On the other hand, if the x,y location is out of range, the entire
     *  point symbol and associated annotation will be hidden.
     *  <p>
     *  For pie slices, if the pie center point (pivot point) of
     *  the slice is in range, the entire slice is drawn, if
     *  it is out of range (and off chart points are not being
     *  displayed) the entire slice is clipped.
     *  <p>
     *  For interpolated or "filled" points, such as those
     *  used in a line or area chart, each interpolated element is
     *  independently clipped. Thus, some, but not all, of
     *  the connecting dots that connect an in-range point
     *  to an out-of-range point would typically be visible
     *  when <tt>showOffChartPoints</tt> is false.
     * 
     * @param showOffChartPoints true to display off-the-chart points,
     *   false (the default) to hide them.
     *
     * @see #getShowOffChartPoints getShowOffChartPoints
     * @see #setShowOffDecoratedChartGlyphs
     *   setShowOffDecoratedChartGlyphs
     */
    public void setShowOffChartPoints(boolean showOffChartPoints) {
      plotPanel.invalidateWidgetsAfterCursor(
         plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
       this.showOffChartPoints = showOffChartPoints;
    }

    /**
     * Defines if this chart will clip any chart elements
     * that extend beyond the bounds of the decorated chart.
     * <p>
     *
     * The decorated chart includes not just the plot area, but
     * space allocated for titles, footnotes, legend key, axis
     * labels, tick marks, etc. The size of this decorated chart
     * can be obtained via the <tt>getXChartSizeDecorated</tt>
     * and <tt>getYChartSizeDecorated</tt> methods.
     *
     * @param showOffDecoratedChartGlyphs use true to
     * clip off-the-decorated-chart symbols, annotations, etc.
     * false to allow such chart elements to be drawn outside
     * of the rectangular region allocated for the chart.
     *
     * @see #getShowOffDecoratedChartGlyphs
     *   getShowOffDecoratedChartGlyphs
     * @see #setShowOffChartPoints setShowOffChartPoints
     * @see #getXChartSizeDecorated getXChartSizeDecorated
     * @see #getYChartSizeDecorated getYChartSizeDecorated
     *
     */

    public void setShowOffDecoratedChartGlyphs(boolean showOffDecoratedChartGlyphs) {
      plotPanel.invalidateWidgetsAfterCursor(
         plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
      this.showOffDecoratedChartGlyphs = showOffDecoratedChartGlyphs;
      chartDecorationsChanged = true;
    }
   

    /**
     * Sets the number of pixels, in the horizontal
     * dimension, available for curve display. Note that
     * this curve display area does <i>not</i> include the
     * axes themselves, their tick marks, their labels, etc.
     *
     * <p>
     *
     * <i>Note</i>: Most modern display devices use "square"
     * pixels, that is, pixels whose width and height are
     * the same. GChart tacitly assumes square pixels in
     * many of its default settings.
     *
     *
     * @param xChartSize the number of x-pixels in the chart region
     *   used for curve display.
     *
     * @see #getXChartSize getXChartSize
     *
     */
    public void setXChartSize(int xChartSize) {
      plotPanel.invalidateWidgetsAfterCursor(
         plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
      plotPanel.plotAreaNeedsUpdate();
      chartDecorationsChanged = true;
      this.xChartSize = xChartSize;
    }

    /**
     * Sets the number of pixels, in the vertical dimension,
     * available for curve display. Note that this curve
     * display region of the chart does <i>not</i> include
     * the axes themselves, their tick marks, labels, etc.
     *
     * <p>
     *
     * <i>Note</i>: Most modern display devices use "square"
     * pixels, that is, pixels whose width and height are
     * the same. GChart tacitly assumes square pixels in
     * many of its default settings.
     *
     * @param yChartSize the number of y-pixels in the chart region
     *   used for curve display.
     *
     * @see #getYChartSize getYChartSize
     *
     */
    public void setYChartSize(int yChartSize) {
      plotPanel.invalidateWidgetsAfterCursor(
         plotPanel.INITIAL_WIDGET_CURSOR_POSITION);
      plotPanel.plotAreaNeedsUpdate();
      chartDecorationsChanged = true;
      this.yChartSize = yChartSize;
    }

    /**
       ** Builds a chart that reflects current user-specified
       ** chart specs (curve data, symbol choices, etc.)
       ** <p>
       ** Before any of the chart specifications of the other
       ** methods of this class will actually be visible
       ** on the chart, you must call this method.
       ** <p>
       ** Typically, for efficiency, you would call this
       ** method only after you had made all of the desired
       ** chart specifications via the other methods.
       **
       ** <p>
       **
       ** By default, updates are optimized for speed, and this
       ** can end up wasting (usually not too much, though there
       ** are exceptions) memory.  To optimize for memory
       ** instead, use the <tt>setOptimizeForMemory</tt> method.
       ** <p>
       **
       ** For a discussion of Client-side GChart update times and
       ** how minimize them, see
       ** <a
       ** href="{@docRoot}/com/googlecode/gchart/client/doc-files/tipsformakingupdatesfaster.html">
       ** Tips for Making Client-side GChart Updates Faster</a>.
       **
       **
       ** @see #setOptimizeForMemory setOptimizeForMemory
       **/
      public void update() {
         assembleChart();
      }
        
    // constructs the chart within the chart panel from current specs
    private void assembleChart() {

       // kick off any dynamic axis limit triggered changes
      if (xAxis.limitsChanged()) {
         xAxis.invalidateDynamicAxisLimits();
         xAxis.rememberLimits();
      }
      if (hasYAxis() && yAxis.limitsChanged()) {
         yAxis.invalidateDynamicAxisLimits();
         yAxis.rememberLimits();
      }
      if (hasY2Axis() && y2Axis.limitsChanged()) {
         y2Axis.invalidateDynamicAxisLimits();
         y2Axis.rememberLimits();
      }
     
      if (chartDecorationsChanged ||
          plotPanel.plotAreaNeedsUpdate) {
        plotPanel.reset(xChartSize, yChartSize,
                        hasYAxis(), hasY2Axis(),
                        xAxis, yAxis, y2Axis);
        GChart.setFontFamily(this,getFontFamily());
        GChart.setBackgroundColor(this, getBackgroundColor());
        GChart.setBorderColor(this, getBorderColor());
        GChart.setBorderStyle(this,getBorderStyle());
        GChart.setBorderWidth(this, getBorderWidth());
        GChart.setPadding(this,getPadding());
        GChart.setOverflow(this, getShowOffDecoratedChartGlyphs() ?
                             "visible" : "hidden");
        this.setPixelSize(getXChartSizeDecorated(),
                          getYChartSizeDecorated());
        
        updateDecorations();
        chartDecorationsChanged = false;
      }
      // actually renders chart, including internal curves used
      // to represent the decorations (title, axis labels, etc.)
      realizePlotPanel();

// To avoid order-of-magnitude FF2 performance hit on busy pages,
// first time, must add plotPanel only AFTER building chart     
      if (plotPanel != chartPanel.getWidget())
         chartPanel.add(plotPanel);
      else
/*
* Without these 2 lines IE7 won't repaint GChart's annotations.
* The lines are not needed in FF2; an IE7 bug is suspected.<p>
*
* I got this workaround from <a href=
* "http://examples.roughian.com">Ian Bambury</a> as part of <a
* href="http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/4c54d8b4aea7f98b/6efd1ab4e5fc0e7b?#6efd1ab4e5fc0e7b">
* this discussion on the GWT forum</a>.
*
*/
        DOM.setStyleAttribute(getElement(), "visibility","hidden");
        DOM.setStyleAttribute(getElement(), "visibility","visible");
      }
    }
         
    // create a Grid representing the chart legend.
    private Grid createLegend(PlotPanel pp) {
       Grid result = new Grid(getNVisibleCurvesOnLegend(), 2);
       int iVisible = 0;
       GChart.setBorderWidth(result,getLegendBorderWidth());
       GChart.setBorderColor(result, getLegendBorderColor());
       GChart.setBorderStyle(result, getLegendBorderStyle());
       GChart.setBackgroundColor(result, getLegendBackgroundColor());
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (c.isVisible() && c.getLegendLabel()!=null) {
             double symBorderFraction =
               c.getSymbol().getBorderWidth()/
                Math.max(
                  Math.max(1.0,c.getSymbol().getFillThickness()),
                  Math.max(c.getSymbol().getWidth(pp),
                           c.getSymbol().getHeight(pp, c.onY2())));
             Image icon =
               c.getSymbol().getSymbolType().createIconImage(
                c.getSymbol(), getLegendFontSize(),
                symBorderFraction);
                 
             result.setWidget(iVisible, 0, icon);
             result.getCellFormatter().setAlignment(iVisible, 0,
                HasHorizontalAlignment.ALIGN_CENTER,
                HasVerticalAlignment.ALIGN_MIDDLE);
            
             HTML label = new HTML(c.getLegendLabel());
             GChart.setFontWeight(label, getLegendFontWeight());
             GChart.setFontStyle(label, getLegendFontStyle());
             GChart.setColor(label, getLegendFontColor());
             GChart.setFontSize(label, getLegendFontSize());
                          
             result.setWidget(iVisible, 1, label);
             result.getCellFormatter().setAlignment(iVisible, 1,
                HasHorizontalAlignment.ALIGN_LEFT,
                HasVerticalAlignment.ALIGN_MIDDLE);

             iVisible++;
          }
       }
       return result;
    }

    // returns char-width-based default legend thickness
    private int getDefaultLegendThickness() {
       final int EXTRA_WIDTH = 5// allow for padding & symbol
       int maxLen = 0;
       for (int i = 0; i < getNCurves(); i++) {
          Curve c = getCurve(i);
          if (c.isVisible() && null != c.getLegendLabel()) {
             maxLen = Math.max(maxLen,
                               htmlWidth(c.getLegendLabel()));
          }
       }
       int result = (int) ((maxLen + EXTRA_WIDTH) *
                           getLegendFontSize() *
                TICK_CHARWIDTH_TO_FONTSIZE_LOWERBOUND);
       return result;
    }

    private int getNVisibleCurvesOnLegend() {
       int result = 0;
       for (int i = 0; i < getNCurves(); i++) {
         if (getCurve(i).isVisible() &&
             getCurve(i).getLegendLabel() != null) result++;
       }
       return result;
    }

    // Defines a default curve border color whenever that color has
    // not been explicitly specified (making every curve the same
    // exact color by default would not be a very good default).
    private void maybeSetDefaultBorderColor(Curve curve, int index) {
       if (curve.getSymbol().borderColorNotSpecified())
          curve.getSymbol().setBorderColor(
              DEFAULT_SYMBOL_BORDER_COLORS[
                     index % DEFAULT_SYMBOL_BORDER_COLORS.length]);
    }


   // renders the curve in the plot panel
   private void realizeCurve(Curve c) {  
     if (!c.isVisible()) return;
     if (c.getWidgetCursor().greaterThanOrEqualTo(
         plotPanel.getFirstInvalidWidgetCursor())) {
       c.getWidgetCursor().copy(plotPanel.getWidgetCursor());
       for (int j = 0; j < c.getNPoints(); j++) {
          c.realizePoint(plotPanel, j);
       }
     }
   }

   
   private void realizePlotPanel() {
     
     plotPanel.beginReusingWidgets();

     //realize the system curves (decorations around the chart)
     systemCurvesAccessible = true;  
     for (int i = -N_SYSTEM_CURVES; i < 0; i++) {
       Curve c = getCurve(i);
       realizeCurve(c);
     }
     systemCurvesAccessible = false;  
     // realize all of the axes, and their labeled ticks
     if (yAxis.getWidgetCursor().greaterThanOrEqualTo(
         plotPanel.getFirstInvalidWidgetCursor())) {
       yAxis.getWidgetCursor().copy(plotPanel.getWidgetCursor());
       if (hasYAxis()) {
         yAxis.maybePopulateTicks();
         yAxis.maybeRealizeAxis(plotPanel);
         for (int j=0; j < yAxis.getTickCount(); j++)
           yAxis.realizeTick(plotPanel, j);
       }
     }
    
     // y2 must come after y, because changes in yAxis label
     // width can move y2 axis location to left or right,
     // so it needs to be updated whenever the y axis changes
     if (y2Axis.getWidgetCursor().greaterThanOrEqualTo(
         plotPanel.getFirstInvalidWidgetCursor())) {
        y2Axis.getWidgetCursor().copy(plotPanel.getWidgetCursor());
        if (hasY2Axis()) {
         y2Axis.maybePopulateTicks();
         y2Axis.maybeRealizeAxis(plotPanel);
         for (int j=0; j < y2Axis.getTickCount(); j++)
            y2Axis.realizeTick(plotPanel, j);
       }
     }
    
     if (xAxis.getWidgetCursor().greaterThanOrEqualTo(
        plotPanel.getFirstInvalidWidgetCursor())) {
        xAxis.getWidgetCursor().copy(plotPanel.getWidgetCursor());
        xAxis.maybePopulateTicks();
        xAxis.maybeRealizeAxis(plotPanel);
        for (int j=0; j < xAxis.getTickCount(); j++)
           xAxis.realizeTick(plotPanel, j);
     }

     // next, create and position each data point's symbol

     setLastPieSliceOrientation(getInitialPieSliceOrientation());
     for (int i = 0; i < getNCurves(); i++) {
       Curve c = getCurve(i);
       realizeCurve(c);
       // keep track of default next orientation for pie slices 
       // (must do this even if we don't have to redraw slice)
       if (c.getSymbol().getSymbolType() instanceof SymbolType.PieSliceSymbolType)
          setLastPieSliceOrientation(
             c.getSymbol().getDecodedPieSliceOrientation()
             + c.getSymbol().getPieSliceSize());
     }
     plotPanel.endReusingWidgets();    
  }
   
} // end of class GChart
TOP

Related Classes of com.googlecode.gchart.client.GChart$Curve$Point

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.