Exemple #1
0
  /**
   * Draws the axis on a Java 2D graphics device (such as the screen or a printer).
   *
   * @param g2 the graphics device.
   * @param cursor the cursor.
   * @param plotArea the area for drawing the axes and data.
   * @param dataArea the area for drawing the data (a subset of the plotArea).
   * @param edge the axis location.
   * @param plotState collects information about the plot (<code>null</code> permitted).
   * @return The updated cursor value.
   */
  public AxisState draw(
      Graphics2D g2,
      double cursor,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      RectangleEdge edge,
      PlotRenderingInfo plotState) {

    // if the axis is not visible, don't draw it...
    if (!isVisible()) {
      AxisState state = new AxisState(cursor);
      // even though the axis is not visible, we need ticks for the
      // gridlines...
      List ticks = refreshTicks(g2, state, dataArea, edge);
      state.setTicks(ticks);
      return state;
    }

    // calculate the adjusted data area taking into account the 3D effect...
    double xOffset = 0.0;
    double yOffset = 0.0;
    Plot plot = getPlot();
    if (plot instanceof CategoryPlot) {
      CategoryPlot cp = (CategoryPlot) plot;
      CategoryItemRenderer r = cp.getRenderer();
      if (r instanceof Effect3D) {
        Effect3D e3D = (Effect3D) r;
        xOffset = e3D.getXOffset();
        yOffset = e3D.getYOffset();
      }
    }

    double adjustedX = dataArea.getMinX();
    double adjustedY = dataArea.getMinY();
    double adjustedW = dataArea.getWidth() - xOffset;
    double adjustedH = dataArea.getHeight() - yOffset;

    if (edge == RectangleEdge.LEFT || edge == RectangleEdge.BOTTOM) {
      adjustedY += yOffset;
    } else if (edge == RectangleEdge.RIGHT || edge == RectangleEdge.TOP) {
      adjustedX += xOffset;
    }
    Rectangle2D adjustedDataArea =
        new Rectangle2D.Double(adjustedX, adjustedY, adjustedW, adjustedH);

    // draw the tick marks and labels...
    AxisState info = drawTickMarksAndLabels(g2, cursor, plotArea, adjustedDataArea, edge);

    // draw the axis label...
    info = drawLabel(getLabel(), g2, plotArea, dataArea, edge, info);

    return info;
  }
  /**
   * Draws the tick marks. This method is called during chart rendering, you normally would not call
   * this method yourself.
   *
   * @param g2 the graphics target ({@code null} not permitted)
   * @param cursor the current offset from the edge of the dataArea
   * @param dataArea the area used for plotting data ({@code null} not permitted)
   * @param edge the location of the axis ({@code null} not permitted)
   * @param state axis state information ({@code null} not permitted)
   * @since 1.0.13
   */
  public void drawTickMarks(
      Graphics2D g2, double cursor, Rectangle2D dataArea, RectangleEdge edge, AxisState state) {

    Plot p = getPlot();
    if (p == null) {
      return;
    }
    CategoryPlot plot = (CategoryPlot) p;
    double il = getTickMarkInsideLength();
    double ol = getTickMarkOutsideLength();
    Line2D line = new Line2D.Double();
    List<Comparable> categories = plot.getCategoriesForAxis(this);
    g2.setPaint(getTickMarkPaint());
    g2.setStroke(getTickMarkStroke());
    if (edge.equals(RectangleEdge.TOP)) {
      for (Comparable key : categories) {
        double x = getCategoryMiddle(key, categories, dataArea, edge);
        line.setLine(x, cursor, x, cursor + il);
        g2.draw(line);
        line.setLine(x, cursor, x, cursor - ol);
        g2.draw(line);
      }
      state.cursorUp(ol);
    } else if (edge.equals(RectangleEdge.BOTTOM)) {
      for (Comparable key : categories) {
        double x = getCategoryMiddle(key, categories, dataArea, edge);
        line.setLine(x, cursor, x, cursor - il);
        g2.draw(line);
        line.setLine(x, cursor, x, cursor + ol);
        g2.draw(line);
      }
      state.cursorDown(ol);
    } else if (edge.equals(RectangleEdge.LEFT)) {
      for (Comparable key : categories) {
        double y = getCategoryMiddle(key, categories, dataArea, edge);
        line.setLine(cursor, y, cursor + il, y);
        g2.draw(line);
        line.setLine(cursor, y, cursor - ol, y);
        g2.draw(line);
      }
      state.cursorLeft(ol);
    } else if (edge.equals(RectangleEdge.RIGHT)) {
      for (Comparable key : categories) {
        double y = getCategoryMiddle(key, categories, dataArea, edge);
        line.setLine(cursor, y, cursor - il, y);
        g2.draw(line);
        line.setLine(cursor, y, cursor + ol, y);
        g2.draw(line);
      }
      state.cursorRight(ol);
    }
  }
  /**
   * Estimates the space required for the axis, given a specific drawing area.
   *
   * @param g2 the graphics device (used to obtain font information).
   * @param plot the plot that the axis belongs to.
   * @param plotArea the area within which the axis should be drawn.
   * @param edge the axis location (top or bottom).
   * @param space the space already reserved.
   * @return The space required to draw the axis.
   */
  @Override
  public AxisSpace reserveSpace(
      Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) {

    // create a new space object if one wasn't supplied...
    if (space == null) {
      space = new AxisSpace();
    }

    // if the axis is not visible, no additional space is required...
    if (!isVisible()) {
      return space;
    }

    // calculate the max size of the tick labels (if visible)...
    double tickLabelHeight = 0.0;
    double tickLabelWidth = 0.0;
    if (isTickLabelsVisible()) {
      g2.setFont(getTickLabelFont());
      AxisState state = new AxisState();
      // we call refresh ticks just to get the maximum width or height
      refreshTicks(g2, state, plotArea, edge);
      if (edge == RectangleEdge.TOP) {
        tickLabelHeight = state.getMax();
      } else if (edge == RectangleEdge.BOTTOM) {
        tickLabelHeight = state.getMax();
      } else if (edge == RectangleEdge.LEFT) {
        tickLabelWidth = state.getMax();
      } else if (edge == RectangleEdge.RIGHT) {
        tickLabelWidth = state.getMax();
      }
    }

    // get the axis label size and update the space object...
    Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge);
    double labelHeight, labelWidth;
    if (RectangleEdge.isTopOrBottom(edge)) {
      labelHeight = labelEnclosure.getHeight();
      space.add(labelHeight + tickLabelHeight + this.categoryLabelPositionOffset, edge);
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      labelWidth = labelEnclosure.getWidth();
      space.add(labelWidth + tickLabelWidth + this.categoryLabelPositionOffset, edge);
    }
    return space;
  }
  /**
   * Creates a temporary list of ticks that can be used when drawing the axis.
   *
   * @param g2 the graphics device (used to get font measurements).
   * @param state the axis state.
   * @param dataArea the area inside the axes.
   * @param edge the location of the axis.
   * @return A list of ticks.
   */
  @Override
  public List<CategoryTick> refreshTicks(
      Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {

    List<CategoryTick> ticks = new java.util.ArrayList<CategoryTick>();

    // sanity check for data area...
    if (dataArea.getHeight() <= 0.0 || dataArea.getWidth() < 0.0) {
      return ticks;
    }

    CategoryPlot plot = (CategoryPlot) getPlot();
    List<Comparable> categories = plot.getCategoriesForAxis(this);
    double max = 0.0;

    if (categories != null) {
      CategoryLabelPosition position = this.categoryLabelPositions.getLabelPosition(edge);
      float r = this.maximumCategoryLabelWidthRatio;
      if (r <= 0.0) {
        r = position.getWidthRatio();
      }

      float l;
      if (position.getWidthType() == CategoryLabelWidthType.CATEGORY) {
        l = (float) calculateCategorySize(categories.size(), dataArea, edge);
      } else {
        if (RectangleEdge.isLeftOrRight(edge)) {
          l = (float) dataArea.getWidth();
        } else {
          l = (float) dataArea.getHeight();
        }
      }
      int categoryIndex = 0;
      for (Comparable category : categories) {
        g2.setFont(getTickLabelFont(category));
        TextBlock label = createLabel(category, l * r, edge, g2);
        if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
          max = Math.max(max, calculateTextBlockHeight(label, position, g2));
        } else if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) {
          max = Math.max(max, calculateTextBlockWidth(label, position, g2));
        }
        ticks.add(
            new CategoryTick(
                category,
                label,
                position.getLabelAnchor(),
                position.getRotationAnchor(),
                position.getAngle()));
        categoryIndex = categoryIndex + 1;
      }
    }
    state.setMax(max);
    return ticks;
  }
  /**
   * 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));
    }
  }
 public AxisState draw(
     Graphics2D g2,
     double cursor,
     Rectangle2D plotArea,
     Rectangle2D dataArea,
     RectangleEdge edge,
     PlotRenderingInfo plotState) {
   AxisState state;
   if (isVisible()) {
     state = drawTickMarksAndLabels(g2, cursor, plotArea, dataArea, edge);
     if (getAttributedLabel() != null) {
       state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state);
     } else {
       state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state);
     }
     createAndAddEntity(cursor, state, dataArea, edge, plotState);
     return state;
   }
   state = new AxisState(cursor);
   state.setTicks(refreshTicks(g2, state, dataArea, edge));
   return state;
 }
  /**
   * Draws the axis label.
   *
   * @param label the label text.
   * @param g2 the graphics device.
   * @param plotArea the plot area.
   * @param dataArea the area inside the axes.
   * @param edge the location of the axis.
   * @param state the axis state (<code>null</code> not permitted).
   * @return Information about the axis.
   */
  protected AxisState drawLabel(
      String label,
      Graphics2D g2,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      RectangleEdge edge,
      AxisState state) {

    // it is unlikely that 'state' will be null, but check anyway...
    ParamChecks.nullNotPermitted(state, "state");

    if ((label == null) || (label.equals(""))) {
      return state;
    }

    Font font = getLabelFont();
    RectangleInsets insets = getLabelInsets();
    g2.setFont(font);
    g2.setPaint(getLabelPaint());
    FontMetrics fm = g2.getFontMetrics();
    Rectangle2D labelBounds = TextUtilities.getTextBounds(label, g2, fm);

    if (edge == RectangleEdge.TOP) {
      AffineTransform t =
          AffineTransform.getRotateInstance(
              getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY());
      Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
      labelBounds = rotatedLabelBounds.getBounds2D();
      double labelx = dataArea.getCenterX();
      double labely = state.getCursor() - insets.getBottom() - labelBounds.getHeight() / 2.0;
      TextUtilities.drawRotatedString(
          label,
          g2,
          (float) labelx,
          (float) labely,
          TextAnchor.CENTER,
          getLabelAngle(),
          TextAnchor.CENTER);
      state.cursorUp(insets.getTop() + labelBounds.getHeight() + insets.getBottom());
    } else if (edge == RectangleEdge.BOTTOM) {
      AffineTransform t =
          AffineTransform.getRotateInstance(
              getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY());
      Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
      labelBounds = rotatedLabelBounds.getBounds2D();
      double labelx = dataArea.getCenterX();
      double labely = state.getCursor() + insets.getTop() + labelBounds.getHeight() / 2.0;
      TextUtilities.drawRotatedString(
          label,
          g2,
          (float) labelx,
          (float) labely,
          TextAnchor.CENTER,
          getLabelAngle(),
          TextAnchor.CENTER);
      state.cursorDown(insets.getTop() + labelBounds.getHeight() + insets.getBottom());
    } else if (edge == RectangleEdge.LEFT) {
      AffineTransform t =
          AffineTransform.getRotateInstance(
              getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY());
      Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
      labelBounds = rotatedLabelBounds.getBounds2D();
      double labelx = state.getCursor() - insets.getRight() - labelBounds.getWidth() / 2.0;
      double labely = dataArea.getCenterY();
      TextUtilities.drawRotatedString(
          label,
          g2,
          (float) labelx,
          (float) labely,
          TextAnchor.CENTER,
          getLabelAngle() - Math.PI / 2.0,
          TextAnchor.CENTER);
      state.cursorLeft(insets.getLeft() + labelBounds.getWidth() + insets.getRight());
    } else if (edge == RectangleEdge.RIGHT) {

      AffineTransform t =
          AffineTransform.getRotateInstance(
              getLabelAngle() + Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY());
      Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
      labelBounds = rotatedLabelBounds.getBounds2D();
      double labelx = state.getCursor() + insets.getLeft() + labelBounds.getWidth() / 2.0;
      double labely = dataArea.getY() + dataArea.getHeight() / 2.0;
      TextUtilities.drawRotatedString(
          label,
          g2,
          (float) labelx,
          (float) labely,
          TextAnchor.CENTER,
          getLabelAngle() + Math.PI / 2.0,
          TextAnchor.CENTER);
      state.cursorRight(insets.getLeft() + labelBounds.getWidth() + insets.getRight());
    }

    return state;
  }
Exemple #8
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 drawSubCategoryLabels(
      Graphics2D g2,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      RectangleEdge edge,
      AxisState state,
      PlotRenderingInfo plotState) {

    if (state == null) {
      throw new IllegalArgumentException("Null 'state' argument.");
    }

    g2.setFont(this.subLabelFont);
    g2.setPaint(this.subLabelPaint);
    CategoryPlot plot = (CategoryPlot) getPlot();
    CategoryDataset dataset = plot.getDataset();
    int categoryCount = dataset.getColumnCount();

    double maxdim = getMaxDim(g2, edge);
    for (int categoryIndex = 0; categoryIndex < categoryCount; categoryIndex++) {

      double x0 = 0.0;
      double x1 = 0.0;
      double y0 = 0.0;
      double y1 = 0.0;
      if (edge == RectangleEdge.TOP) {
        x0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge);
        y1 = state.getCursor();
        y0 = y1 - maxdim;
      } else if (edge == RectangleEdge.BOTTOM) {
        x0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge);
        y0 = state.getCursor();
        y1 = y0 + maxdim;
      } else if (edge == RectangleEdge.LEFT) {
        y0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge);
        x1 = state.getCursor();
        x0 = x1 - maxdim;
      } else if (edge == RectangleEdge.RIGHT) {
        y0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge);
        x0 = state.getCursor();
        x1 = x0 + maxdim;
      }
      Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
      int subCategoryCount = this.subCategories.size();
      float width = (float) ((x1 - x0) / subCategoryCount);
      float height = (float) ((y1 - y0) / subCategoryCount);
      float xx = 0.0f;
      float yy = 0.0f;
      for (int i = 0; i < subCategoryCount; i++) {
        if (RectangleEdge.isTopOrBottom(edge)) {
          xx = (float) (x0 + (i + 0.5) * width);
          yy = (float) area.getCenterY();
        } else {
          xx = (float) area.getCenterX();
          yy = (float) (y0 + (i + 0.5) * height);
        }
        String label = this.subCategories.get(i).toString();
        TextUtilities.drawRotatedString(
            label, g2, xx, yy, TextAnchor.CENTER, 0.0, TextAnchor.CENTER);
      }
    }

    if (edge.equals(RectangleEdge.TOP)) {
      double h = maxdim;
      state.cursorUp(h);
    } else if (edge.equals(RectangleEdge.BOTTOM)) {
      double h = maxdim;
      state.cursorDown(h);
    } else if (edge == RectangleEdge.LEFT) {
      double w = maxdim;
      state.cursorLeft(w);
    } else if (edge == RectangleEdge.RIGHT) {
      double w = maxdim;
      state.cursorRight(w);
    }
    return state;
  }
Exemple #9
0
  /**
   * Draws the plot on a Java 2D graphics device (such as the screen or a printer).
   *
   * @param g2 the graphics device.
   * @param cursor the cursor.
   * @param plotArea the area within which the chart should be drawn.
   * @param dataArea the area within which the plot should be drawn (a subset of the drawArea).
   * @param reservedArea the reserved area.
   * @param edge the color bar location.
   * @return The new cursor location.
   */
  public double draw(
      Graphics2D g2,
      double cursor,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      Rectangle2D reservedArea,
      RectangleEdge edge) {

    Rectangle2D colorBarArea = null;

    double thickness = calculateBarThickness(dataArea, edge);
    if (this.colorBarThickness > 0) {
      thickness = this.colorBarThickness; // allow fixed thickness
    }

    double length = 0.0;
    if (RectangleEdge.isLeftOrRight(edge)) {
      length = dataArea.getHeight();
    } else {
      length = dataArea.getWidth();
    }

    if (this.colorBarLength > 0) {
      length = this.colorBarLength;
    }

    if (edge == RectangleEdge.BOTTOM) {
      colorBarArea =
          new Rectangle2D.Double(
              dataArea.getX(), plotArea.getMaxY() + this.outerGap, length, thickness);
    } else if (edge == RectangleEdge.TOP) {
      colorBarArea =
          new Rectangle2D.Double(
              dataArea.getX(), reservedArea.getMinY() + this.outerGap, length, thickness);
    } else if (edge == RectangleEdge.LEFT) {
      colorBarArea =
          new Rectangle2D.Double(
              plotArea.getX() - thickness - this.outerGap, dataArea.getMinY(), thickness, length);
    } else if (edge == RectangleEdge.RIGHT) {
      colorBarArea =
          new Rectangle2D.Double(
              plotArea.getMaxX() + this.outerGap, dataArea.getMinY(), thickness, length);
    }

    // update, but dont draw tick marks (needed for stepped colors)
    this.axis.refreshTicks(g2, new AxisState(), colorBarArea, edge);

    drawColorBar(g2, colorBarArea, edge);

    AxisState state = null;
    if (edge == RectangleEdge.TOP) {
      cursor = colorBarArea.getMinY();
      state = this.axis.draw(g2, cursor, reservedArea, colorBarArea, RectangleEdge.TOP, null);
    } else if (edge == RectangleEdge.BOTTOM) {
      cursor = colorBarArea.getMaxY();
      state = this.axis.draw(g2, cursor, reservedArea, colorBarArea, RectangleEdge.BOTTOM, null);
    } else if (edge == RectangleEdge.LEFT) {
      cursor = colorBarArea.getMinX();
      state = this.axis.draw(g2, cursor, reservedArea, colorBarArea, RectangleEdge.LEFT, null);
    } else if (edge == RectangleEdge.RIGHT) {
      cursor = colorBarArea.getMaxX();
      state = this.axis.draw(g2, cursor, reservedArea, colorBarArea, RectangleEdge.RIGHT, null);
    }
    return state.getCursor();
  }
  /**
   * Draws the tick labels for one "band" of time periods.
   *
   * @param band the band index (zero-based).
   * @param g2 the graphics device.
   * @param state the axis state.
   * @param dataArea the data area.
   * @param edge the edge where the axis is located.
   * @return The updated axis state.
   */
  protected AxisState drawTickLabels(
      int band, Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {

    // work out the initial gap
    double delta1 = 0.0;
    FontMetrics fm = g2.getFontMetrics(this.labelInfo[band].getLabelFont());
    if (edge == RectangleEdge.BOTTOM) {
      delta1 = this.labelInfo[band].getPadding().calculateTopOutset(fm.getHeight());
    } else if (edge == RectangleEdge.TOP) {
      delta1 = this.labelInfo[band].getPadding().calculateBottomOutset(fm.getHeight());
    }
    state.moveCursor(delta1, edge);
    long axisMin = this.first.getFirstMillisecond();
    long axisMax = this.last.getLastMillisecond();
    g2.setFont(this.labelInfo[band].getLabelFont());
    g2.setPaint(this.labelInfo[band].getLabelPaint());

    // work out the number of periods to skip for labelling
    RegularTimePeriod p1 =
        this.labelInfo[band].createInstance(new Date(axisMin), this.timeZone, this.locale);
    RegularTimePeriod p2 =
        this.labelInfo[band].createInstance(new Date(axisMax), this.timeZone, this.locale);
    String label1 =
        this.labelInfo[band].getDateFormat().format(new Date(p1.getMiddleMillisecond()));
    String label2 =
        this.labelInfo[band].getDateFormat().format(new Date(p2.getMiddleMillisecond()));
    Rectangle2D b1 = TextUtilities.getTextBounds(label1, g2, g2.getFontMetrics());
    Rectangle2D b2 = TextUtilities.getTextBounds(label2, g2, g2.getFontMetrics());
    double w = Math.max(b1.getWidth(), b2.getWidth());
    long ww = Math.round(java2DToValue(dataArea.getX() + w + 5.0, dataArea, edge));
    if (isInverted()) {
      ww = axisMax - ww;
    } else {
      ww = ww - axisMin;
    }
    long length = p1.getLastMillisecond() - p1.getFirstMillisecond();
    int periods = (int) (ww / length) + 1;

    RegularTimePeriod p =
        this.labelInfo[band].createInstance(new Date(axisMin), this.timeZone, this.locale);
    Rectangle2D b = null;
    long lastXX = 0L;
    float y = (float) (state.getCursor());
    TextAnchor anchor = TextAnchor.TOP_CENTER;
    float yDelta = (float) b1.getHeight();
    if (edge == RectangleEdge.TOP) {
      anchor = TextAnchor.BOTTOM_CENTER;
      yDelta = -yDelta;
    }
    while (p.getFirstMillisecond() <= axisMax) {
      float x = (float) valueToJava2D(p.getMiddleMillisecond(), dataArea, edge);
      DateFormat df = this.labelInfo[band].getDateFormat();
      String label = df.format(new Date(p.getMiddleMillisecond()));
      long first = p.getFirstMillisecond();
      long last = p.getLastMillisecond();
      if (last > axisMax) {
        // this is the last period, but it is only partially visible
        // so check that the label will fit before displaying it...
        Rectangle2D bb = TextUtilities.getTextBounds(label, g2, g2.getFontMetrics());
        if ((x + bb.getWidth() / 2) > dataArea.getMaxX()) {
          float xstart = (float) valueToJava2D(Math.max(first, axisMin), dataArea, edge);
          if (bb.getWidth() < (dataArea.getMaxX() - xstart)) {
            x = ((float) dataArea.getMaxX() + xstart) / 2.0f;
          } else {
            label = null;
          }
        }
      }
      if (first < axisMin) {
        // this is the first period, but it is only partially visible
        // so check that the label will fit before displaying it...
        Rectangle2D bb = TextUtilities.getTextBounds(label, g2, g2.getFontMetrics());
        if ((x - bb.getWidth() / 2) < dataArea.getX()) {
          float xlast = (float) valueToJava2D(Math.min(last, axisMax), dataArea, edge);
          if (bb.getWidth() < (xlast - dataArea.getX())) {
            x = (xlast + (float) dataArea.getX()) / 2.0f;
          } else {
            label = null;
          }
        }
      }
      if (label != null) {
        g2.setPaint(this.labelInfo[band].getLabelPaint());
        b = TextUtilities.drawAlignedString(label, g2, x, y, anchor);
      }
      if (lastXX > 0L) {
        if (this.labelInfo[band].getDrawDividers()) {
          long nextXX = p.getFirstMillisecond();
          long mid = (lastXX + nextXX) / 2;
          float mid2d = (float) valueToJava2D(mid, dataArea, edge);
          g2.setStroke(this.labelInfo[band].getDividerStroke());
          g2.setPaint(this.labelInfo[band].getDividerPaint());
          g2.draw(new Line2D.Float(mid2d, y, mid2d, y + yDelta));
        }
      }
      lastXX = last;
      for (int i = 0; i < periods; i++) {
        p = p.next();
      }
      p.peg(this.calendar);
    }
    double used = 0.0;
    if (b != null) {
      used = b.getHeight();
      // work out the trailing gap
      if (edge == RectangleEdge.BOTTOM) {
        used += this.labelInfo[band].getPadding().calculateBottomOutset(fm.getHeight());
      } else if (edge == RectangleEdge.TOP) {
        used += this.labelInfo[band].getPadding().calculateTopOutset(fm.getHeight());
      }
    }
    state.moveCursor(used, edge);
    return state;
  }
 /**
  * Draws the major and minor tick marks for an axis that lies at the top or bottom of the plot.
  *
  * @param g2 the graphics device.
  * @param state the axis state.
  * @param dataArea the data area.
  * @param edge the edge.
  */
 protected void drawTickMarksHorizontal(
     Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
   List<ValueTick> ticks = new ArrayList<ValueTick>();
   double x0;
   double y0 = state.getCursor();
   double insideLength = getTickMarkInsideLength();
   double outsideLength = getTickMarkOutsideLength();
   RegularTimePeriod t =
       createInstance(
           this.majorTickTimePeriodClass, this.first.getStart(), getTimeZone(), this.locale);
   long t0 = t.getFirstMillisecond();
   Line2D inside = null;
   Line2D outside = null;
   long firstOnAxis = getFirst().getFirstMillisecond();
   long lastOnAxis = getLast().getLastMillisecond() + 1;
   while (t0 <= lastOnAxis) {
     ticks.add(new NumberTick((double) t0, "", TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
     x0 = valueToJava2D(t0, dataArea, edge);
     if (edge == RectangleEdge.TOP) {
       inside = new Line2D.Double(x0, y0, x0, y0 + insideLength);
       outside = new Line2D.Double(x0, y0, x0, y0 - outsideLength);
     } else if (edge == RectangleEdge.BOTTOM) {
       inside = new Line2D.Double(x0, y0, x0, y0 - insideLength);
       outside = new Line2D.Double(x0, y0, x0, y0 + outsideLength);
     }
     if (t0 >= firstOnAxis) {
       g2.setPaint(getTickMarkPaint());
       g2.setStroke(getTickMarkStroke());
       g2.draw(inside);
       g2.draw(outside);
     }
     // draw minor tick marks
     if (this.minorTickMarksVisible) {
       RegularTimePeriod tminor =
           createInstance(this.minorTickTimePeriodClass, new Date(t0), getTimeZone(), this.locale);
       long tt0 = tminor.getFirstMillisecond();
       while (tt0 < t.getLastMillisecond() && tt0 < lastOnAxis) {
         double xx0 = valueToJava2D(tt0, dataArea, edge);
         if (edge == RectangleEdge.TOP) {
           inside = new Line2D.Double(xx0, y0, xx0, y0 + this.minorTickMarkInsideLength);
           outside = new Line2D.Double(xx0, y0, xx0, y0 - this.minorTickMarkOutsideLength);
         } else if (edge == RectangleEdge.BOTTOM) {
           inside = new Line2D.Double(xx0, y0, xx0, y0 - this.minorTickMarkInsideLength);
           outside = new Line2D.Double(xx0, y0, xx0, y0 + this.minorTickMarkOutsideLength);
         }
         if (tt0 >= firstOnAxis) {
           g2.setPaint(this.minorTickMarkPaint);
           g2.setStroke(this.minorTickMarkStroke);
           g2.draw(inside);
           g2.draw(outside);
         }
         tminor = tminor.next();
         tminor.peg(this.calendar);
         tt0 = tminor.getFirstMillisecond();
       }
     }
     t = t.next();
     t.peg(this.calendar);
     t0 = t.getFirstMillisecond();
   }
   if (edge == RectangleEdge.TOP) {
     state.cursorUp(Math.max(outsideLength, this.minorTickMarkOutsideLength));
   } else if (edge == RectangleEdge.BOTTOM) {
     state.cursorDown(Math.max(outsideLength, this.minorTickMarkOutsideLength));
   }
   state.setTicks(ticks);
 }
  /**
   * 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;
  }