Esempio n. 1
0
  /**
   * Created an entity for the axis.
   *
   * @param cursor the initial cursor value.
   * @param state the axis state after completion of the drawing with a possibly updated cursor
   *     position.
   * @param dataArea the data area.
   * @param edge the edge.
   * @param plotState the PlotRenderingInfo from which a reference to the entity collection can be
   *     obtained.
   * @since 1.0.13
   */
  protected void createAndAddEntity(
      double cursor,
      AxisState state,
      Rectangle2D dataArea,
      RectangleEdge edge,
      PlotRenderingInfo plotState) {

    if (plotState == null || plotState.getOwner() == null) {
      return; // no need to create entity if we can´t save it anyways...
    }
    Rectangle2D hotspot = null;
    if (edge.equals(RectangleEdge.TOP)) {
      hotspot =
          new Rectangle2D.Double(
              dataArea.getX(), state.getCursor(), dataArea.getWidth(), cursor - state.getCursor());
    } else if (edge.equals(RectangleEdge.BOTTOM)) {
      hotspot =
          new Rectangle2D.Double(
              dataArea.getX(), cursor, dataArea.getWidth(), state.getCursor() - cursor);
    } else if (edge.equals(RectangleEdge.LEFT)) {
      hotspot =
          new Rectangle2D.Double(
              state.getCursor(), dataArea.getY(), cursor - state.getCursor(), dataArea.getHeight());
    } else if (edge.equals(RectangleEdge.RIGHT)) {
      hotspot =
          new Rectangle2D.Double(
              cursor, dataArea.getY(), state.getCursor() - cursor, dataArea.getHeight());
    }
    EntityCollection e = plotState.getOwner().getEntityCollection();
    if (e != null) {
      e.add(new AxisEntity(hotspot, this));
    }
  }
Esempio n. 2
0
  /**
   * Handle the case where a plot implements the {@link Zoomable} interface.
   *
   * @param zoomable the zoomable plot.
   * @param e the mouse wheel event.
   */
  private void handleZoomable(Zoomable zoomable, MouseWheelEvent e) {
    // don't zoom unless the mouse pointer is in the plot's data area
    ChartRenderingInfo info = this.chartPanel.getChartRenderingInfo();
    PlotRenderingInfo pinfo = info.getPlotInfo();
    Point2D p = this.chartPanel.translateScreenToJava2D(e.getPoint());
    if (!pinfo.getDataArea().contains(p)) {
      return;
    }

    Plot plot = (Plot) zoomable;
    // do not notify while zooming each axis
    boolean notifyState = plot.isNotify();
    plot.setNotify(false);
    int clicks = e.getWheelRotation();
    double zf = 1.0 + this.zoomFactor;
    if (clicks < 0) {
      zf = 1.0 / zf;
    }
    if (chartPanel.isDomainZoomable()) {
      zoomable.zoomDomainAxes(zf, pinfo, p, true);
    }
    if (chartPanel.isRangeZoomable()) {
      zoomable.zoomRangeAxes(zf, pinfo, p, true);
    }
    plot.setNotify(notifyState); // this generates the change event too
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the data is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot ({@code null} permitted).
   * @param pass the pass index.
   */
  @Override
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    // do nothing if item is not visible
    if (!getItemVisible(series, item)) {
      return;
    }

    // first pass draws the background (lines, for instance)
    if (isLinePass(pass)) {
      if (getItemLineVisible(series, item)) {
        if (this.drawSeriesLineAsPath) {
          drawPrimaryLineAsPath(
              state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea);
        } else {
          drawPrimaryLine(
              state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea);
        }
      }
    }
    // second pass adds shapes where the items are ..
    else if (isItemPass(pass)) {

      // setup for collecting optional entity info...
      EntityCollection entities = null;
      if (info != null && info.getOwner() != null) {
        entities = info.getOwner().getEntityCollection();
      }

      drawSecondaryPass(
          g2,
          plot,
          dataset,
          pass,
          series,
          item,
          domainAxis,
          dataArea,
          rangeAxis,
          crosshairState,
          entities);
    }
  }
 /**
  * Returns the data area (the area inside the axes) for the plot or subplot.
  *
  * @param point the selection point (for subplot selection).
  * @return The data area.
  */
 public Rectangle2D findDataArea(Point2D point) {
   PlotRenderingInfo plotInfo = this.info.getPlotInfo();
   Rectangle2D result;
   if (plotInfo.getSubplotCount() == 0) {
     result = plotInfo.getDataArea();
   } else {
     int subplotIndex = plotInfo.getSubplotIndex(point);
     if (subplotIndex == -1) {
       return null;
     }
     result = plotInfo.getSubplotInfo(subplotIndex).getDataArea();
   }
   return result;
 }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the data is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain (horizontal) axis.
   * @param rangeAxis the range (vertical) axis.
   * @param dataset the dataset (an {@link XYZDataset} is expected).
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
   * @param pass the pass index.
   */
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    // return straight away if the item is not visible
    if (!getItemVisible(series, item)) {
      return;
    }

    PlotOrientation orientation = plot.getOrientation();

    // get the data point...
    double x = dataset.getXValue(series, item);
    double y = dataset.getYValue(series, item);
    double z = Double.NaN;
    if (dataset instanceof XYZDataset) {
      XYZDataset xyzData = (XYZDataset) dataset;
      z = xyzData.getZValue(series, item);
    }
    if (!Double.isNaN(z)) {
      RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
      RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
      double transX = domainAxis.valueToJava2D(x, dataArea, domainAxisLocation);
      double transY = rangeAxis.valueToJava2D(y, dataArea, rangeAxisLocation);

      double transDomain = 0.0;
      double transRange = 0.0;
      double zero;

      switch (getScaleType()) {
        case SCALE_ON_DOMAIN_AXIS:
          zero = domainAxis.valueToJava2D(0.0, dataArea, domainAxisLocation);
          transDomain = domainAxis.valueToJava2D(z, dataArea, domainAxisLocation) - zero;
          transRange = transDomain;
          break;
        case SCALE_ON_RANGE_AXIS:
          zero = rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation);
          transRange = zero - rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation);
          transDomain = transRange;
          break;
        default:
          double zero1 = domainAxis.valueToJava2D(0.0, dataArea, domainAxisLocation);
          double zero2 = rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation);
          transDomain = domainAxis.valueToJava2D(z, dataArea, domainAxisLocation) - zero1;
          transRange = zero2 - rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation);
      }
      transDomain = Math.abs(transDomain);
      transRange = Math.abs(transRange);
      Ellipse2D circle = null;
      if (orientation == PlotOrientation.VERTICAL) {
        circle =
            new Ellipse2D.Double(
                transX - transDomain / 2.0, transY - transRange / 2.0, transDomain, transRange);
      } else if (orientation == PlotOrientation.HORIZONTAL) {
        circle =
            new Ellipse2D.Double(
                transY - transRange / 2.0, transX - transDomain / 2.0, transRange, transDomain);
      }
      g2.setPaint(getItemPaint(series, item));
      g2.fill(circle);
      g2.setStroke(getItemOutlineStroke(series, item));
      g2.setPaint(getItemOutlinePaint(series, item));
      g2.draw(circle);

      if (isItemLabelVisible(series, item)) {
        if (orientation == PlotOrientation.VERTICAL) {
          drawItemLabel(g2, orientation, dataset, series, item, transX, transY, false);
        } else if (orientation == PlotOrientation.HORIZONTAL) {
          drawItemLabel(g2, orientation, dataset, series, item, transY, transX, false);
        }
      }

      // add an entity if this info is being collected
      EntityCollection entities = null;
      if (info != null) {
        entities = info.getOwner().getEntityCollection();
        if (entities != null && circle.intersects(dataArea)) {
          addEntity(
              entities, circle, dataset, series, item, circle.getCenterX(), circle.getCenterY());
        }
      }

      int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
      int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
      updateCrosshairValues(
          crosshairState, x, y, domainAxisIndex, rangeAxisIndex, transX, transY, orientation);
    }
  }
  /**
   * Draws the block representing the specified item.
   *
   * @param g2 the graphics device.
   * @param state the state.
   * @param dataArea the data area.
   * @param info the plot rendering info.
   * @param plot the plot.
   * @param domainAxis the x-axis.
   * @param rangeAxis the y-axis.
   * @param dataset the dataset.
   * @param series the series index.
   * @param item the item index.
   * @param crosshairState the crosshair state.
   * @param pass the pass index.
   */
  @Override
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    double x = dataset.getXValue(series, item);
    double y = dataset.getYValue(series, item);
    double dx = 0.0;
    double dy = 0.0;
    if (dataset instanceof VectorXYDataset) {
      dx = ((VectorXYDataset) dataset).getVectorXValue(series, item);
      dy = ((VectorXYDataset) dataset).getVectorYValue(series, item);
    }
    double xx0 = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge());
    double yy0 = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge());
    double xx1 = domainAxis.valueToJava2D(x + dx, dataArea, plot.getDomainAxisEdge());
    double yy1 = rangeAxis.valueToJava2D(y + dy, dataArea, plot.getRangeAxisEdge());
    Line2D line;
    PlotOrientation orientation = plot.getOrientation();
    if (orientation.equals(PlotOrientation.HORIZONTAL)) {
      line = new Line2D.Double(yy0, xx0, yy1, xx1);
    } else {
      line = new Line2D.Double(xx0, yy0, xx1, yy1);
    }
    g2.setPaint(getItemPaint(series, item));
    g2.setStroke(getItemStroke(series, item));
    g2.draw(line);

    // calculate the arrow head and draw it...
    double dxx = (xx1 - xx0);
    double dyy = (yy1 - yy0);
    double bx = xx0 + (1.0 - this.baseLength) * dxx;
    double by = yy0 + (1.0 - this.baseLength) * dyy;

    double cx = xx0 + (1.0 - this.headLength) * dxx;
    double cy = yy0 + (1.0 - this.headLength) * dyy;

    double angle = 0.0;
    if (dxx != 0.0) {
      angle = Math.PI / 2.0 - Math.atan(dyy / dxx);
    }
    double deltaX = 2.0 * Math.cos(angle);
    double deltaY = 2.0 * Math.sin(angle);

    double leftx = cx + deltaX;
    double lefty = cy - deltaY;
    double rightx = cx - deltaX;
    double righty = cy + deltaY;

    GeneralPath p = new GeneralPath();
    if (orientation == PlotOrientation.VERTICAL) {
      p.moveTo((float) xx1, (float) yy1);
      p.lineTo((float) rightx, (float) righty);
      p.lineTo((float) bx, (float) by);
      p.lineTo((float) leftx, (float) lefty);
    } else { // orientation is HORIZONTAL
      p.moveTo((float) yy1, (float) xx1);
      p.lineTo((float) righty, (float) rightx);
      p.lineTo((float) by, (float) bx);
      p.lineTo((float) lefty, (float) leftx);
    }
    p.closePath();
    g2.draw(p);

    // setup for collecting optional entity info...
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
      if (entities != null) {
        addEntity(entities, line.getBounds(), dataset, series, item, 0.0, 0.0);
      }
    }
  }
  /**
   * Draws the visual representation of a single data item, second pass. In the second pass, the
   * renderer draws the lines and shapes for the individual points in the two series.
   *
   * @param x_graphics the graphics device.
   * @param x_dataArea the area within which the data is being drawn.
   * @param x_info collects information about the drawing.
   * @param x_plot the plot (can be used to obtain standard color information etc).
   * @param x_domainAxis the domain (horizontal) axis.
   * @param x_rangeAxis the range (vertical) axis.
   * @param x_dataset the dataset.
   * @param x_series the series index (zero-based).
   * @param x_item the item index (zero-based).
   * @param x_crosshairState crosshair information for the plot (<code>null</code> permitted).
   */
  protected void drawItemPass1(
      Graphics2D x_graphics,
      Rectangle2D x_dataArea,
      PlotRenderingInfo x_info,
      XYPlot x_plot,
      ValueAxis x_domainAxis,
      ValueAxis x_rangeAxis,
      XYDataset x_dataset,
      int x_series,
      int x_item,
      CrosshairState x_crosshairState) {

    Shape l_entityArea = null;
    EntityCollection l_entities = null;
    if (null != x_info) {
      l_entities = x_info.getOwner().getEntityCollection();
    }

    Paint l_seriesPaint = getItemPaint(x_series, x_item);
    Stroke l_seriesStroke = getItemStroke(x_series, x_item);
    x_graphics.setPaint(l_seriesPaint);
    x_graphics.setStroke(l_seriesStroke);

    PlotOrientation l_orientation = x_plot.getOrientation();
    RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
    RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge();

    double l_x0 = x_dataset.getXValue(x_series, x_item);
    double l_y0 = x_dataset.getYValue(x_series, x_item);
    double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, l_domainAxisLocation);
    double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea, l_rangeAxisLocation);

    if (getShapesVisible()) {
      Shape l_shape = getItemShape(x_series, x_item);
      if (l_orientation == PlotOrientation.HORIZONTAL) {
        l_shape = ShapeUtilities.createTranslatedShape(l_shape, l_y1, l_x1);
      } else {
        l_shape = ShapeUtilities.createTranslatedShape(l_shape, l_x1, l_y1);
      }
      if (l_shape.intersects(x_dataArea)) {
        x_graphics.setPaint(getItemPaint(x_series, x_item));
        x_graphics.fill(l_shape);
      }
      l_entityArea = l_shape;
    }

    // add an entity for the item...
    if (null != l_entities) {
      if (null == l_entityArea) {
        l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), 4, 4);
      }
      String l_tip = null;
      XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, x_item);
      if (null != l_tipGenerator) {
        l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, x_item);
      }
      String l_url = null;
      XYURLGenerator l_urlGenerator = getURLGenerator();
      if (null != l_urlGenerator) {
        l_url = l_urlGenerator.generateURL(x_dataset, x_series, x_item);
      }
      XYItemEntity l_entity =
          new XYItemEntity(l_entityArea, x_dataset, x_series, x_item, l_tip, l_url);
      l_entities.add(l_entity);
    }

    // draw the item label if there is one...
    if (isItemLabelVisible(x_series, x_item)) {
      drawItemLabel(
          x_graphics, l_orientation, x_dataset, x_series, x_item, l_x1, l_y1, (l_y1 < 0.0));
    }

    int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis);
    int l_rangeAxisIndex = x_plot.getRangeAxisIndex(x_rangeAxis);
    updateCrosshairValues(
        x_crosshairState,
        l_x0,
        l_y0,
        l_domainAxisIndex,
        l_rangeAxisIndex,
        l_x1,
        l_y1,
        l_orientation);

    if (0 == x_item) {
      return;
    }

    double l_x2 =
        x_domainAxis.valueToJava2D(
            x_dataset.getXValue(x_series, (x_item - 1)), x_dataArea, l_domainAxisLocation);
    double l_y2 =
        x_rangeAxis.valueToJava2D(
            x_dataset.getYValue(x_series, (x_item - 1)), x_dataArea, l_rangeAxisLocation);

    Line2D l_line = null;
    if (PlotOrientation.HORIZONTAL == l_orientation) {
      l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2);
    } else if (PlotOrientation.VERTICAL == l_orientation) {
      l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2);
    }

    if ((null != l_line) && l_line.intersects(x_dataArea)) {
      x_graphics.setPaint(getItemPaint(x_series, x_item));
      x_graphics.setStroke(getItemStroke(x_series, x_item));
      x_graphics.draw(l_line);
    }
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the data is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
   * @param pass the pass index.
   */
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    if (!getItemVisible(series, item)) {
      return;
    }
    // setup for collecting optional entity info...
    Shape entityArea = null;
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
    }

    PlotOrientation orientation = plot.getOrientation();
    Paint paint = getItemPaint(series, item);
    Stroke seriesStroke = getItemStroke(series, item);
    g2.setPaint(paint);
    g2.setStroke(seriesStroke);

    // get the data point...
    double x1 = dataset.getXValue(series, item);
    double y1 = dataset.getYValue(series, item);
    if (Double.isNaN(x1) || Double.isNaN(y1)) {
      return;
    }

    RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
    double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
    double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);

    if (getPlotLines()) {
      if (item == 0) {
        if (this.drawSeriesLineAsPath) {
          State s = (State) state;
          s.seriesPath.reset();
          s.lastPointGood = false;
        }
      }

      if (this.drawSeriesLineAsPath) {
        State s = (State) state;
        // update path to reflect latest point
        if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) {
          float x = (float) transX1;
          float y = (float) transY1;
          if (orientation == PlotOrientation.HORIZONTAL) {
            x = (float) transY1;
            y = (float) transX1;
          }
          if (s.isLastPointGood()) {
            // TODO: check threshold
            s.seriesPath.lineTo(x, y);
          } else {
            s.seriesPath.moveTo(x, y);
          }
          s.setLastPointGood(true);
        } else {
          s.setLastPointGood(false);
        }
        if (item == dataset.getItemCount(series) - 1) {
          // draw path
          g2.setStroke(getSeriesStroke(series));
          g2.setPaint(getSeriesPaint(series));
          g2.draw(s.seriesPath);
        }
      } else if (item != 0) {
        // get the previous data point...
        double x0 = dataset.getXValue(series, item - 1);
        double y0 = dataset.getYValue(series, item - 1);
        if (!Double.isNaN(x0) && !Double.isNaN(y0)) {
          boolean drawLine = true;
          if (getPlotDiscontinuous()) {
            // only draw a line if the gap between the current and
            // previous data point is within the threshold
            int numX = dataset.getItemCount(series);
            double minX = dataset.getXValue(series, 0);
            double maxX = dataset.getXValue(series, numX - 1);
            if (this.gapThresholdType == UnitType.ABSOLUTE) {
              drawLine = Math.abs(x1 - x0) <= this.gapThreshold;
            } else {
              drawLine = Math.abs(x1 - x0) <= ((maxX - minX) / numX * getGapThreshold());
            }
          }
          if (drawLine) {
            double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
            double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);

            // only draw if we have good values
            if (Double.isNaN(transX0)
                || Double.isNaN(transY0)
                || Double.isNaN(transX1)
                || Double.isNaN(transY1)) {
              return;
            }

            if (orientation == PlotOrientation.HORIZONTAL) {
              state.workingLine.setLine(transY0, transX0, transY1, transX1);
            } else if (orientation == PlotOrientation.VERTICAL) {
              state.workingLine.setLine(transX0, transY0, transX1, transY1);
            }

            if (state.workingLine.intersects(dataArea)) {
              g2.draw(state.workingLine);
            }
          }
        }
      }
    }

    if (getPlotShapes()) {

      Shape shape = getItemShape(series, item);
      if (orientation == PlotOrientation.HORIZONTAL) {
        shape = ShapeUtilities.createTranslatedShape(shape, transY1, transX1);
      } else if (orientation == PlotOrientation.VERTICAL) {
        shape = ShapeUtilities.createTranslatedShape(shape, transX1, transY1);
      }
      if (shape.intersects(dataArea)) {
        if (getItemShapeFilled(series, item)) {
          g2.fill(shape);
        } else {
          g2.draw(shape);
        }
      }
      entityArea = shape;
    }

    if (getPlotImages()) {
      Image image = getImage(plot, series, item, transX1, transY1);
      if (image != null) {
        Point hotspot = getImageHotspot(plot, series, item, transX1, transY1, image);
        g2.drawImage(
            image, (int) (transX1 - hotspot.getX()), (int) (transY1 - hotspot.getY()), null);
        entityArea =
            new Rectangle2D.Double(
                transX1 - hotspot.getX(),
                transY1 - hotspot.getY(),
                image.getWidth(null),
                image.getHeight(null));
      }
    }

    // draw the item label if there is one...
    if (isItemLabelVisible(series, item)) {
      double xx = transX1;
      double yy = transY1;
      if (orientation == PlotOrientation.HORIZONTAL) {
        xx = transY1;
        yy = transX1;
      }
      drawItemLabel(g2, orientation, dataset, series, item, xx, yy, (y1 < 0.0));
    }

    updateCrosshairValues(crosshairState, x1, y1, transX1, transY1, orientation);

    // add an entity for the item...
    if (entities != null) {
      addEntity(entities, entityArea, dataset, series, item, transX1, transY1);
    }
  }
  /**
   * Draws the plot on a Java 2D graphics device (such as the screen or a printer).
   *
   * @param g2 the graphics device.
   * @param area the area within which the plot should be drawn.
   * @param anchor the anchor point (<code>null</code> permitted).
   * @param parentState the state from the parent plot, if there is one.
   * @param info collects info about the drawing.
   */
  public void draw(
      Graphics2D g2,
      Rectangle2D area,
      Point2D anchor,
      PlotState parentState,
      PlotRenderingInfo info) {

    // adjust the drawing area for the plot insets (if any)...
    RectangleInsets insets = getInsets();
    insets.trim(area);
    drawBackground(g2, area);
    drawOutline(g2, area);

    // check that there is some data to display...
    if (DatasetUtilities.isEmptyOrNull(getDataset())) {
      drawNoDataMessage(g2, area);
      return;
    }

    int pieCount = 0;
    if (getDataExtractOrder() == TableOrder.BY_ROW) {
      pieCount = getDataset().getRowCount();
    } else {
      pieCount = getDataset().getColumnCount();
    }

    // the columns variable is always >= rows
    int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
    int displayRows = (int) Math.ceil((double) pieCount / (double) displayCols);

    // swap rows and columns to match plotArea shape
    if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
      int temp = displayCols;
      displayCols = displayRows;
      displayRows = temp;
    }

    prefetchSectionPaints();

    int x = (int) area.getX();
    int y = (int) area.getY();
    int width = ((int) area.getWidth()) / displayCols;
    int height = ((int) area.getHeight()) / displayRows;
    int row = 0;
    int column = 0;
    int diff = (displayRows * displayCols) - pieCount;
    int xoffset = 0;
    Rectangle rect = new Rectangle();

    for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
      rect.setBounds(x + xoffset + (width * column), y + (height * row), width, height);

      String title = null;
      if (getDataExtractOrder() == TableOrder.BY_ROW) {
        title = getDataset().getRowKey(pieIndex).toString();
      } else {
        title = getDataset().getColumnKey(pieIndex).toString();
      }
      getPieChart().setTitle(title);

      PieDataset piedataset = null;
      PieDataset dd = new CategoryToPieDataset(getDataset(), getDataExtractOrder(), pieIndex);
      if (getLimit() > 0.0) {
        piedataset =
            DatasetUtilities.createConsolidatedPieDataset(dd, getAggregatedItemsKey(), getLimit());
      } else {
        piedataset = dd;
      }
      PiePlot piePlot = (PiePlot) getPieChart().getPlot();
      piePlot.setDataset(piedataset);
      piePlot.setPieIndex(pieIndex);

      // update the section colors to match the global colors...
      for (int i = 0; i < piedataset.getItemCount(); i++) {
        Comparable key = piedataset.getKey(i);
        Paint p;
        if (key.equals(getAggregatedItemsKey())) {
          p = getAggregatedItemsPaint();
        } else {
          p = (Paint) this.sectionPaints.get(key);
        }
        piePlot.setSectionPaint(key, p);
      }

      ChartRenderingInfo subinfo = null;
      if (info != null) {
        subinfo = new ChartRenderingInfo();
      }
      getPieChart().draw(g2, rect, subinfo);
      if (info != null) {
        info.getOwner().getEntityCollection().addAll(subinfo.getEntityCollection());
        info.addSubplotInfo(subinfo.getPlotInfo());
      }

      ++column;
      if (column == displayCols) {
        column = 0;
        ++row;

        if (row == displayRows - 1 && diff != 0) {
          xoffset = (diff * width) / 2;
        }
      }
    }
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the plot is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
   * @param pass the pass index (ignored here).
   */
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    // setup for collecting optional entity info...
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
    }

    IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;

    double x = intervalDataset.getXValue(series, item);
    double yLow = intervalDataset.getStartYValue(series, item);
    double yHigh = intervalDataset.getEndYValue(series, item);

    RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    RectangleEdge yAxisLocation = plot.getRangeAxisEdge();

    double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
    double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation);
    double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation);

    Paint p = getItemPaint(series, item);
    Stroke s = getItemStroke(series, item);

    Line2D line = null;
    Shape shape = getItemShape(series, item);
    Shape top = null;
    Shape bottom = null;
    PlotOrientation orientation = plot.getOrientation();
    if (orientation == PlotOrientation.HORIZONTAL) {
      line = new Line2D.Double(yyLow, xx, yyHigh, xx);
      top = ShapeUtilities.createTranslatedShape(shape, yyHigh, xx);
      bottom = ShapeUtilities.createTranslatedShape(shape, yyLow, xx);
    } else if (orientation == PlotOrientation.VERTICAL) {
      line = new Line2D.Double(xx, yyLow, xx, yyHigh);
      top = ShapeUtilities.createTranslatedShape(shape, xx, yyHigh);
      bottom = ShapeUtilities.createTranslatedShape(shape, xx, yyLow);
    }
    g2.setPaint(p);
    g2.setStroke(s);
    g2.draw(line);

    g2.fill(top);
    g2.fill(bottom);

    // for item labels, we have a special case because there is the
    // possibility to draw (a) the regular item label near to just the
    // upper y-value, or (b) the regular item label near the upper y-value
    // PLUS an additional item label near the lower y-value.
    if (isItemLabelVisible(series, item)) {
      drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh, false);
      drawAdditionalItemLabel(g2, orientation, dataset, series, item, xx, yyLow);
    }

    // add an entity for the item...
    if (entities != null) {
      addEntity(entities, line.getBounds(), dataset, series, item, 0.0, 0.0);
    }
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the data is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState information about crosshairs on a plot.
   * @param pass the pass index.
   */
  @Override
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    // setup for collecting optional entity info...
    Shape entityArea;
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
    }

    TableXYDataset tdataset = (TableXYDataset) dataset;
    PlotOrientation orientation = plot.getOrientation();

    // get the data point...
    double x1 = dataset.getXValue(series, item);
    double y1 = dataset.getYValue(series, item);
    if (Double.isNaN(y1)) {
      y1 = 0.0;
    }
    double[] stack1 = getStackValues(tdataset, series, item);

    // get the previous point and the next point so we can calculate a
    // "hot spot" for the area (used by the chart entity)...
    double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
    double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
    if (Double.isNaN(y0)) {
      y0 = 0.0;
    }
    double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1, 0));

    int itemCount = dataset.getItemCount(series);
    double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1));
    double y2 = dataset.getYValue(series, Math.min(item + 1, itemCount - 1));
    if (Double.isNaN(y2)) {
      y2 = 0.0;
    }
    double[] stack2 = getStackValues(tdataset, series, Math.min(item + 1, itemCount - 1));

    double xleft = (x0 + x1) / 2.0;
    double xright = (x1 + x2) / 2.0;
    double[] stackLeft = averageStackValues(stack0, stack1);
    double[] stackRight = averageStackValues(stack1, stack2);
    double[] adjStackLeft = adjustedStackValues(stack0, stack1);
    double[] adjStackRight = adjustedStackValues(stack1, stack2);

    RectangleEdge edge0 = plot.getDomainAxisEdge();

    float transX1 = (float) domainAxis.valueToJava2D(x1, dataArea, edge0);
    float transXLeft = (float) domainAxis.valueToJava2D(xleft, dataArea, edge0);
    float transXRight = (float) domainAxis.valueToJava2D(xright, dataArea, edge0);

    if (this.roundXCoordinates) {
      transX1 = Math.round(transX1);
      transXLeft = Math.round(transXLeft);
      transXRight = Math.round(transXRight);
    }
    float transY1;

    RectangleEdge edge1 = plot.getRangeAxisEdge();

    GeneralPath left = new GeneralPath();
    GeneralPath right = new GeneralPath();
    if (y1 >= 0.0) { // handle positive value
      transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea, edge1);
      float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1], dataArea, edge1);
      float transStackLeft = (float) rangeAxis.valueToJava2D(adjStackLeft[1], dataArea, edge1);

      // LEFT POLYGON
      if (y0 >= 0.0) {
        double yleft = (y0 + y1) / 2.0 + stackLeft[1];
        float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1);
        if (orientation == PlotOrientation.VERTICAL) {
          left.moveTo(transX1, transY1);
          left.lineTo(transX1, transStack1);
          left.lineTo(transXLeft, transStackLeft);
          left.lineTo(transXLeft, transYLeft);
        } else {
          left.moveTo(transY1, transX1);
          left.lineTo(transStack1, transX1);
          left.lineTo(transStackLeft, transXLeft);
          left.lineTo(transYLeft, transXLeft);
        }
        left.closePath();
      } else {
        if (orientation == PlotOrientation.VERTICAL) {
          left.moveTo(transX1, transStack1);
          left.lineTo(transX1, transY1);
          left.lineTo(transXLeft, transStackLeft);
        } else {
          left.moveTo(transStack1, transX1);
          left.lineTo(transY1, transX1);
          left.lineTo(transStackLeft, transXLeft);
        }
        left.closePath();
      }

      float transStackRight = (float) rangeAxis.valueToJava2D(adjStackRight[1], dataArea, edge1);
      // RIGHT POLYGON
      if (y2 >= 0.0) {
        double yright = (y1 + y2) / 2.0 + stackRight[1];
        float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1);
        if (orientation == PlotOrientation.VERTICAL) {
          right.moveTo(transX1, transStack1);
          right.lineTo(transX1, transY1);
          right.lineTo(transXRight, transYRight);
          right.lineTo(transXRight, transStackRight);
        } else {
          right.moveTo(transStack1, transX1);
          right.lineTo(transY1, transX1);
          right.lineTo(transYRight, transXRight);
          right.lineTo(transStackRight, transXRight);
        }
        right.closePath();
      } else {
        if (orientation == PlotOrientation.VERTICAL) {
          right.moveTo(transX1, transStack1);
          right.lineTo(transX1, transY1);
          right.lineTo(transXRight, transStackRight);
        } else {
          right.moveTo(transStack1, transX1);
          right.lineTo(transY1, transX1);
          right.lineTo(transStackRight, transXRight);
        }
        right.closePath();
      }
    } else { // handle negative value
      transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea, edge1);
      float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0], dataArea, edge1);
      float transStackLeft = (float) rangeAxis.valueToJava2D(adjStackLeft[0], dataArea, edge1);

      // LEFT POLYGON
      if (y0 >= 0.0) {
        if (orientation == PlotOrientation.VERTICAL) {
          left.moveTo(transX1, transStack1);
          left.lineTo(transX1, transY1);
          left.lineTo(transXLeft, transStackLeft);
        } else {
          left.moveTo(transStack1, transX1);
          left.lineTo(transY1, transX1);
          left.lineTo(transStackLeft, transXLeft);
        }
        left.clone();
      } else {
        double yleft = (y0 + y1) / 2.0 + stackLeft[0];
        float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1);
        if (orientation == PlotOrientation.VERTICAL) {
          left.moveTo(transX1, transY1);
          left.lineTo(transX1, transStack1);
          left.lineTo(transXLeft, transStackLeft);
          left.lineTo(transXLeft, transYLeft);
        } else {
          left.moveTo(transY1, transX1);
          left.lineTo(transStack1, transX1);
          left.lineTo(transStackLeft, transXLeft);
          left.lineTo(transYLeft, transXLeft);
        }
        left.closePath();
      }
      float transStackRight = (float) rangeAxis.valueToJava2D(adjStackRight[0], dataArea, edge1);

      // RIGHT POLYGON
      if (y2 >= 0.0) {
        if (orientation == PlotOrientation.VERTICAL) {
          right.moveTo(transX1, transStack1);
          right.lineTo(transX1, transY1);
          right.lineTo(transXRight, transStackRight);
        } else {
          right.moveTo(transStack1, transX1);
          right.lineTo(transY1, transX1);
          right.lineTo(transStackRight, transXRight);
        }
        right.closePath();
      } else {
        double yright = (y1 + y2) / 2.0 + stackRight[0];
        float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1);
        if (orientation == PlotOrientation.VERTICAL) {
          right.moveTo(transX1, transStack1);
          right.lineTo(transX1, transY1);
          right.lineTo(transXRight, transYRight);
          right.lineTo(transXRight, transStackRight);
        } else {
          right.moveTo(transStack1, transX1);
          right.lineTo(transY1, transX1);
          right.lineTo(transYRight, transXRight);
          right.lineTo(transStackRight, transXRight);
        }
        right.closePath();
      }
    }

    //  Get series Paint and Stroke
    Paint itemPaint = getItemPaint(series, item);
    if (pass == 0) {
      g2.setPaint(itemPaint);
      g2.fill(left);
      g2.fill(right);
    }

    // add an entity for the item...
    if (entities != null) {
      GeneralPath gp = new GeneralPath(left);
      gp.append(right, false);
      entityArea = gp;
      addEntity(entities, entityArea, dataset, series, item, transX1, transY1);
    }
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the plot is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
   * @param pass the pass index.
   */
  @Override
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    double x = dataset.getXValue(series, item);
    if (!domainAxis.getRange().contains(x)) {
      return; // the x value is not within the axis range
    }
    double xx = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge());

    // setup for collecting optional entity info...
    Shape entityArea = null;
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
    }

    PlotOrientation orientation = plot.getOrientation();
    RectangleEdge location = plot.getRangeAxisEdge();

    Paint itemPaint = getItemPaint(series, item);
    Stroke itemStroke = getItemStroke(series, item);
    g2.setPaint(itemPaint);
    g2.setStroke(itemStroke);

    if (dataset instanceof OHLCDataset) {
      OHLCDataset hld = (OHLCDataset) dataset;

      double yHigh = hld.getHighValue(series, item);
      double yLow = hld.getLowValue(series, item);
      if (!Double.isNaN(yHigh) && !Double.isNaN(yLow)) {
        double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, location);
        double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, location);
        if (orientation == PlotOrientation.HORIZONTAL) {
          g2.draw(new Line2D.Double(yyLow, xx, yyHigh, xx));
          entityArea =
              new Rectangle2D.Double(
                  Math.min(yyLow, yyHigh), xx - 1.0, Math.abs(yyHigh - yyLow), 2.0);
        } else if (orientation == PlotOrientation.VERTICAL) {
          g2.draw(new Line2D.Double(xx, yyLow, xx, yyHigh));
          entityArea =
              new Rectangle2D.Double(
                  xx - 1.0, Math.min(yyLow, yyHigh), 2.0, Math.abs(yyHigh - yyLow));
        }
      }

      double delta = getTickLength();
      if (domainAxis.isInverted()) {
        delta = -delta;
      }
      if (getDrawOpenTicks()) {
        double yOpen = hld.getOpenValue(series, item);
        if (!Double.isNaN(yOpen)) {
          double yyOpen = rangeAxis.valueToJava2D(yOpen, dataArea, location);
          if (this.openTickPaint != null) {
            g2.setPaint(this.openTickPaint);
          } else {
            g2.setPaint(itemPaint);
          }
          if (orientation == PlotOrientation.HORIZONTAL) {
            g2.draw(new Line2D.Double(yyOpen, xx + delta, yyOpen, xx));
          } else if (orientation == PlotOrientation.VERTICAL) {
            g2.draw(new Line2D.Double(xx - delta, yyOpen, xx, yyOpen));
          }
        }
      }

      if (getDrawCloseTicks()) {
        double yClose = hld.getCloseValue(series, item);
        if (!Double.isNaN(yClose)) {
          double yyClose = rangeAxis.valueToJava2D(yClose, dataArea, location);
          if (this.closeTickPaint != null) {
            g2.setPaint(this.closeTickPaint);
          } else {
            g2.setPaint(itemPaint);
          }
          if (orientation == PlotOrientation.HORIZONTAL) {
            g2.draw(new Line2D.Double(yyClose, xx, yyClose, xx - delta));
          } else if (orientation == PlotOrientation.VERTICAL) {
            g2.draw(new Line2D.Double(xx, yyClose, xx + delta, yyClose));
          }
        }
      }

    } else {
      // not a HighLowDataset, so just draw a line connecting this point
      // with the previous point...
      if (item > 0) {
        double x0 = dataset.getXValue(series, item - 1);
        double y0 = dataset.getYValue(series, item - 1);
        double y = dataset.getYValue(series, item);
        if (Double.isNaN(x0) || Double.isNaN(y0) || Double.isNaN(y)) {
          return;
        }
        double xx0 = domainAxis.valueToJava2D(x0, dataArea, plot.getDomainAxisEdge());
        double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location);
        double yy = rangeAxis.valueToJava2D(y, dataArea, location);
        if (orientation == PlotOrientation.HORIZONTAL) {
          g2.draw(new Line2D.Double(yy0, xx0, yy, xx));
        } else if (orientation == PlotOrientation.VERTICAL) {
          g2.draw(new Line2D.Double(xx0, yy0, xx, yy));
        }
      }
    }

    if (entities != null) {
      addEntity(entities, entityArea, dataset, series, item, 0.0, 0.0);
    }
  }
  /**
   * Draws the visual representation of a single data item.
   *
   * @param g2 the graphics device.
   * @param state the renderer state.
   * @param dataArea the area within which the plot is being drawn.
   * @param info collects information about the drawing.
   * @param plot the plot (can be used to obtain standard color information etc).
   * @param domainAxis the domain axis.
   * @param rangeAxis the range axis.
   * @param dataset the dataset.
   * @param series the series index (zero-based).
   * @param item the item index (zero-based).
   * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
   * @param pass the pass index.
   */
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    if (!(dataset instanceof IntervalXYDataset && dataset instanceof TableXYDataset)) {
      String message = "dataset (type " + dataset.getClass().getName() + ") has wrong type:";
      boolean and = false;
      if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) {
        message += " it is no IntervalXYDataset";
        and = true;
      }
      if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) {
        if (and) {
          message += " and";
        }
        message += " it is no TableXYDataset";
      }

      throw new IllegalArgumentException(message);
    }

    IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
    double value = intervalDataset.getYValue(series, item);
    if (Double.isNaN(value)) {
      return;
    }

    // if we are rendering the values as percentages, we need to calculate
    // the total for the current item.  Unfortunately here we end up
    // repeating the calculation more times than is strictly necessary -
    // hopefully I'll come back to this and find a way to add the
    // total(s) to the renderer state.  The other problem is we implicitly
    // assume the dataset has no negative values...perhaps that can be
    // fixed too.
    double total = 0.0;
    if (this.renderAsPercentages) {
      total = DatasetUtilities.calculateStackTotal((TableXYDataset) dataset, item);
      value = value / total;
    }

    double positiveBase = 0.0;
    double negativeBase = 0.0;

    for (int i = 0; i < series; i++) {
      double v = dataset.getYValue(i, item);
      if (!Double.isNaN(v)) {
        if (this.renderAsPercentages) {
          v = v / total;
        }
        if (v > 0) {
          positiveBase = positiveBase + v;
        } else {
          negativeBase = negativeBase + v;
        }
      }
    }

    double translatedBase;
    double translatedValue;
    RectangleEdge edgeR = plot.getRangeAxisEdge();
    if (value > 0.0) {
      translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, edgeR);
      translatedValue = rangeAxis.valueToJava2D(positiveBase + value, dataArea, edgeR);
    } else {
      translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, edgeR);
      translatedValue = rangeAxis.valueToJava2D(negativeBase + value, dataArea, edgeR);
    }

    RectangleEdge edgeD = plot.getDomainAxisEdge();
    double startX = intervalDataset.getStartXValue(series, item);
    if (Double.isNaN(startX)) {
      return;
    }
    double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, edgeD);

    double endX = intervalDataset.getEndXValue(series, item);
    if (Double.isNaN(endX)) {
      return;
    }
    double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD);

    double translatedWidth = Math.max(1, Math.abs(translatedEndX - translatedStartX));
    double translatedHeight = Math.abs(translatedValue - translatedBase);
    if (getMargin() > 0.0) {
      double cut = translatedWidth * getMargin();
      translatedWidth = translatedWidth - cut;
      translatedStartX = translatedStartX + cut / 2;
    }

    Rectangle2D bar = null;
    PlotOrientation orientation = plot.getOrientation();
    if (orientation == PlotOrientation.HORIZONTAL) {
      bar =
          new Rectangle2D.Double(
              Math.min(translatedBase, translatedValue),
              Math.min(translatedEndX, translatedStartX),
              translatedHeight,
              translatedWidth);
    } else if (orientation == PlotOrientation.VERTICAL) {
      bar =
          new Rectangle2D.Double(
              Math.min(translatedStartX, translatedEndX),
              Math.min(translatedBase, translatedValue),
              translatedWidth,
              translatedHeight);
    }
    boolean positive = (value > 0.0);
    boolean inverted = rangeAxis.isInverted();
    RectangleEdge barBase;
    if (orientation == PlotOrientation.HORIZONTAL) {
      if (positive && inverted || !positive && !inverted) {
        barBase = RectangleEdge.RIGHT;
      } else {
        barBase = RectangleEdge.LEFT;
      }
    } else {
      if (positive && !inverted || !positive && inverted) {
        barBase = RectangleEdge.BOTTOM;
      } else {
        barBase = RectangleEdge.TOP;
      }
    }

    if (pass == 0) {
      if (getShadowsVisible()) {
        getBarPainter().paintBarShadow(g2, this, series, item, bar, barBase, false);
      }
    } else if (pass == 1) {
      getBarPainter().paintBar(g2, this, series, item, bar, barBase);

      // add an entity for the item...
      if (info != null) {
        EntityCollection entities = info.getOwner().getEntityCollection();
        if (entities != null) {
          addEntity(entities, bar, dataset, series, item, bar.getCenterX(), bar.getCenterY());
        }
      }
    } else if (pass == 2) {
      // handle item label drawing, now that we know all the bars have
      // been drawn...
      if (isItemLabelVisible(series, item)) {
        XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
        drawItemLabel(g2, dataset, series, item, plot, generator, bar, value < 0.0);
      }
    }
  }
  /**
   * Draws the category labels and returns the updated axis state.
   *
   * @param g2 the graphics device (<code>null</code> not permitted).
   * @param plotArea the plot area (<code>null</code> not permitted).
   * @param dataArea the area inside the axes (<code>null</code> not permitted).
   * @param edge the axis location (<code>null</code> not permitted).
   * @param state the axis state (<code>null</code> not permitted).
   * @param plotState collects information about the plot (<code>null</code> permitted).
   * @return The updated axis state (never <code>null</code>).
   */
  protected AxisState drawCategoryLabels(
      Graphics2D g2,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      RectangleEdge edge,
      AxisState state,
      PlotRenderingInfo plotState) {

    ParamChecks.nullNotPermitted(state, "state");
    if (!isTickLabelsVisible()) {
      return state;
    }
    List<CategoryTick> ticks = refreshTicks(g2, state, plotArea, edge);
    // state.setTicks(ticks);        //FIXME MMC had to remove this as the types don't match

    int categoryIndex = 0;
    for (CategoryTick tick : ticks) {

      g2.setFont(getTickLabelFont(tick.getCategory()));
      g2.setPaint(getTickLabelPaint(tick.getCategory()));

      CategoryLabelPosition position = this.categoryLabelPositions.getLabelPosition(edge);
      double x0 = 0.0;
      double x1 = 0.0;
      double y0 = 0.0;
      double y1 = 0.0;
      if (edge == RectangleEdge.TOP) {
        x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        y1 = state.getCursor() - this.categoryLabelPositionOffset;
        y0 = y1 - state.getMax();
      } else if (edge == RectangleEdge.BOTTOM) {
        x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        y0 = state.getCursor() + this.categoryLabelPositionOffset;
        y1 = y0 + state.getMax();
      } else if (edge == RectangleEdge.LEFT) {
        y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        x1 = state.getCursor() - this.categoryLabelPositionOffset;
        x0 = x1 - state.getMax();
      } else if (edge == RectangleEdge.RIGHT) {
        y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        x0 = state.getCursor() + this.categoryLabelPositionOffset;
        x1 = x0 - state.getMax();
      }
      Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
      Point2D anchorPoint = RectangleAnchor.coordinates(area, position.getCategoryAnchor());
      TextBlock block = tick.getLabel();
      block.draw(
          g2,
          (float) anchorPoint.getX(),
          (float) anchorPoint.getY(),
          position.getLabelAnchor(),
          (float) anchorPoint.getX(),
          (float) anchorPoint.getY(),
          position.getAngle());
      Shape bounds =
          block.calculateBounds(
              g2,
              (float) anchorPoint.getX(),
              (float) anchorPoint.getY(),
              position.getLabelAnchor(),
              (float) anchorPoint.getX(),
              (float) anchorPoint.getY(),
              position.getAngle());
      if (plotState != null && plotState.getOwner() != null) {
        EntityCollection entities = plotState.getOwner().getEntityCollection();
        if (entities != null) {
          String tooltip = getCategoryLabelToolTip(tick.getCategory());
          String url = getCategoryLabelURL(tick.getCategory());
          entities.add(new CategoryLabelEntity(tick.getCategory(), bounds, tooltip, url));
        }
      }
      categoryIndex++;
    }

    if (edge.equals(RectangleEdge.TOP)) {
      double h = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorUp(h);
    } else if (edge.equals(RectangleEdge.BOTTOM)) {
      double h = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorDown(h);
    } else if (edge == RectangleEdge.LEFT) {
      double w = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorLeft(w);
    } else if (edge == RectangleEdge.RIGHT) {
      double w = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorRight(w);
    }
    return state;
  }
  /**
   * Draws the block representing the specified item.
   *
   * @param g2 the graphics device.
   * @param state the state.
   * @param dataArea the data area.
   * @param info the plot rendering info.
   * @param plot the plot.
   * @param domainAxis the x-axis.
   * @param rangeAxis the y-axis.
   * @param dataset the dataset.
   * @param series the series index.
   * @param item the item index.
   * @param crosshairState the crosshair state.
   * @param pass the pass index.
   */
  public void drawItem(
      Graphics2D g2,
      XYItemRendererState state,
      Rectangle2D dataArea,
      PlotRenderingInfo info,
      XYPlot plot,
      ValueAxis domainAxis,
      ValueAxis rangeAxis,
      XYDataset dataset,
      int series,
      int item,
      CrosshairState crosshairState,
      int pass) {

    Shape hotspot = null;
    EntityCollection entities = null;
    if (info != null) {
      entities = info.getOwner().getEntityCollection();
    }

    double x = dataset.getXValue(series, item);
    double y = dataset.getYValue(series, item);
    if (Double.isNaN(x) || Double.isNaN(y)) {
      // can't draw anything
      return;
    }

    double transX = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge());
    double transY = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge());

    PlotOrientation orientation = plot.getOrientation();

    // draw optional guide lines
    if ((pass == 0) && this.guideLinesVisible) {
      g2.setStroke(this.guideLineStroke);
      g2.setPaint(this.guideLinePaint);
      if (orientation == PlotOrientation.HORIZONTAL) {
        g2.draw(new Line2D.Double(transY, dataArea.getMinY(), transY, dataArea.getMaxY()));
        g2.draw(new Line2D.Double(dataArea.getMinX(), transX, dataArea.getMaxX(), transX));
      } else {
        g2.draw(new Line2D.Double(transX, dataArea.getMinY(), transX, dataArea.getMaxY()));
        g2.draw(new Line2D.Double(dataArea.getMinX(), transY, dataArea.getMaxX(), transY));
      }
    } else if (pass == 1) {
      Shape shape = getItemShape(series, item);
      if (orientation == PlotOrientation.HORIZONTAL) {
        shape = ShapeUtilities.createTranslatedShape(shape, transY, transX);
      } else if (orientation == PlotOrientation.VERTICAL) {
        shape = ShapeUtilities.createTranslatedShape(shape, transX, transY);
      }
      hotspot = shape;
      if (shape.intersects(dataArea)) {
        // if (getItemShapeFilled(series, item)) {
        g2.setPaint(getPaint(dataset, series, item));
        g2.fill(shape);
        // }
        if (this.drawOutlines) {
          if (getUseOutlinePaint()) {
            g2.setPaint(getItemOutlinePaint(series, item));
          } else {
            g2.setPaint(getItemPaint(series, item));
          }
          g2.setStroke(getItemOutlineStroke(series, item));
          g2.draw(shape);
        }
      }

      // add an entity for the item...
      if (entities != null) {
        addEntity(entities, hotspot, dataset, series, item, transX, transY);
      }
    }
  }