コード例 #1
0
  /** Fetches the latest legend items. */
  protected void fetchLegendItems() {
    this.items.clear();
    RectangleEdge p = getPosition();
    if (RectangleEdge.isTopOrBottom(p)) {
      this.items.setArrangement(this.hLayout);
    } else {
      this.items.setArrangement(this.vLayout);
    }

    if (this.sortOrder.equals(SortOrder.ASCENDING)) {
      for (int s = 0; s < this.sources.length; s++) {
        LegendItemCollection legendItems = this.sources[s].getLegendItems();
        if (legendItems != null) {
          for (int i = 0; i < legendItems.getItemCount(); i++) {
            addItemBlock(legendItems.get(i));
          }
        }
      }
    } else {
      for (int s = this.sources.length - 1; s >= 0; s--) {
        LegendItemCollection legendItems = this.sources[s].getLegendItems();
        if (legendItems != null) {
          for (int i = legendItems.getItemCount() - 1; i >= 0; i--) {
            addItemBlock(legendItems.get(i));
          }
        }
      }
    }
  }
コード例 #2
0
  /**
   * Converts a data value to a coordinate in Java2D space, assuming that the axis runs along one
   * edge of the specified plotArea. Note that it is possible for the coordinate to fall outside the
   * plotArea.
   *
   * @param value the data value.
   * @param plotArea the area for plotting the data.
   * @param edge the axis location.
   * @return The Java2D coordinate.
   */
  @Override
  public double valueToJava2D(double value, Rectangle2D plotArea, RectangleEdge edge) {

    Range range = getRange();
    double axisMin = switchedLog10(range.getLowerBound());
    double axisMax = switchedLog10(range.getUpperBound());

    double min = 0.0;
    double max = 0.0;
    if (RectangleEdge.isTopOrBottom(edge)) {
      min = plotArea.getMinX();
      max = plotArea.getMaxX();
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      min = plotArea.getMaxY();
      max = plotArea.getMinY();
    }

    value = switchedLog10(value);

    if (isInverted()) {
      return max - (((value - axisMin) / (axisMax - axisMin)) * (max - min));
    } else {
      return min + (((value - axisMin) / (axisMax - axisMin)) * (max - min));
    }
  }
コード例 #3
0
  /**
   * Reserve some space on each axis side because we draw a centered label at each extremity.
   *
   * @param g2 the graphics device.
   * @param plot the plot.
   * @param plotArea the plot area.
   * @param edge the edge.
   * @param space the space already reserved.
   * @return The reserved space.
   */
  public AxisSpace reserveSpace(
      Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) {

    this.internalMarkerCycleBoundTick = null;
    AxisSpace ret = super.reserveSpace(g2, plot, plotArea, edge, space);
    if (this.internalMarkerCycleBoundTick == null) {
      return ret;
    }

    FontMetrics fm = g2.getFontMetrics(getTickLabelFont());
    Rectangle2D r =
        TextUtilities.getTextBounds(this.internalMarkerCycleBoundTick.getText(), g2, fm);

    if (RectangleEdge.isTopOrBottom(edge)) {
      if (isVerticalTickLabels()) {
        space.add(r.getHeight() / 2, RectangleEdge.RIGHT);
      } else {
        space.add(r.getWidth() / 2, RectangleEdge.RIGHT);
      }
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      if (isVerticalTickLabels()) {
        space.add(r.getWidth() / 2, RectangleEdge.TOP);
      } else {
        space.add(r.getHeight() / 2, RectangleEdge.TOP);
      }
    }

    return ret;
  }
コード例 #4
0
  /**
   * Converts a coordinate in Java2D space to the corresponding data value, assuming that the axis
   * runs along one edge of the specified plotArea.
   *
   * @param java2DValue the coordinate in Java2D space.
   * @param plotArea the area in which the data is plotted.
   * @param edge the axis location.
   * @return The data value.
   */
  @Override
  public double java2DToValue(double java2DValue, Rectangle2D plotArea, RectangleEdge edge) {

    Range range = getRange();
    double axisMin = switchedLog10(range.getLowerBound());
    double axisMax = switchedLog10(range.getUpperBound());

    double plotMin = 0.0;
    double plotMax = 0.0;
    if (RectangleEdge.isTopOrBottom(edge)) {
      plotMin = plotArea.getX();
      plotMax = plotArea.getMaxX();
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      plotMin = plotArea.getMaxY();
      plotMax = plotArea.getMinY();
    }

    if (isInverted()) {
      return switchedPow10(
          axisMax - ((java2DValue - plotMin) / (plotMax - plotMin)) * (axisMax - axisMin));
    } else {
      return switchedPow10(
          axisMin + ((java2DValue - plotMin) / (plotMax - plotMin)) * (axisMax - axisMin));
    }
  }
コード例 #5
0
  /**
   * Converts a coordinate from Java 2D space to data space.
   *
   * @param java2DValue the coordinate in Java2D space.
   * @param dataArea the data area.
   * @param edge the edge.
   * @return The data value.
   */
  public double java2DToValue(double java2DValue, Rectangle2D dataArea, RectangleEdge edge) {
    Range range = getRange();

    double vmax = range.getUpperBound();
    double vp = getCycleBound();

    double jmin = 0.0;
    double jmax = 0.0;
    if (RectangleEdge.isTopOrBottom(edge)) {
      jmin = dataArea.getMinX();
      jmax = dataArea.getMaxX();
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      jmin = dataArea.getMaxY();
      jmax = dataArea.getMinY();
    }

    if (isInverted()) {
      double jbreak = jmax - (vmax - vp) * (jmax - jmin) / this.period;
      if (java2DValue >= jbreak) {
        return vp + (jmax - java2DValue) * this.period / (jmax - jmin);
      } else {
        return vp - (java2DValue - jmin) * this.period / (jmax - jmin);
      }
    } else {
      double jbreak = (vmax - vp) * (jmax - jmin) / this.period + jmin;
      if (java2DValue <= jbreak) {
        return vp + (java2DValue - jmin) * this.period / (jmax - jmin);
      } else {
        return vp - (jmax - java2DValue) * this.period / (jmax - jmin);
      }
    }
  }
 /**
  * Selects an appropriate tick value for the axis. The strategy is to display as many ticks as
  * possible (selected from an array of 'standard' tick units) without the labels overlapping.
  *
  * @param g2 the graphics device.
  * @param dataArea the area defined by the axes.
  * @param edge the axis location.
  * @since 1.0.7
  */
 protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) {
   if (RectangleEdge.isTopOrBottom(edge)) {
     selectHorizontalAutoTickUnit(g2, dataArea, edge);
   } else if (RectangleEdge.isLeftOrRight(edge)) {
     selectVerticalAutoTickUnit(g2, dataArea, edge);
   }
 }
コード例 #7
0
 /**
  * Draws the tick marks for the axis.
  *
  * @param g2 the graphics device.
  * @param state the axis state.
  * @param dataArea the data area.
  * @param edge the edge.
  */
 protected void drawTickMarks(
     Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
   if (RectangleEdge.isTopOrBottom(edge)) {
     drawTickMarksHorizontal(g2, state, dataArea, edge);
   } else if (RectangleEdge.isLeftOrRight(edge)) {
     drawTickMarksVertical(g2, state, dataArea, edge);
   }
 }
 /**
  * Calculates the positions of the tick labels for the axis, storing the results in the tick label
  * list (ready for drawing).
  *
  * @param g2 the graphics device.
  * @param state the axis state.
  * @param dataArea the area in which the plot should be drawn.
  * @param edge the location of the axis.
  * @return A list of ticks.
  */
 @Override
 public List refreshTicks(
     Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
   List result = new java.util.ArrayList();
   if (RectangleEdge.isTopOrBottom(edge)) {
     result = refreshTicksHorizontal(g2, dataArea, edge);
   } else if (RectangleEdge.isLeftOrRight(edge)) {
     result = refreshTicksVertical(g2, dataArea, edge);
   }
   return result;
 }
コード例 #9
0
  /**
   * Estimates the space (height or width) required to draw the axis.
   *
   * @param g2 the graphics device.
   * @param plot the plot that the axis belongs to.
   * @param plotArea the area within which the plot (including axes) should be drawn.
   * @param edge the axis location.
   * @param space space already reserved.
   * @return The space required to draw the axis (including pre-reserved space).
   */
  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;
    }

    // if the axis has a fixed dimension, return it...
    double dimension = getFixedDimension();
    if (dimension > 0.0) {
      space.ensureAtLeast(dimension, edge);
    }

    // get the axis label size and update the space object...
    Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge);
    double labelHeight = 0.0;
    double labelWidth = 0.0;
    double tickLabelBandsDimension = 0.0;

    for (int i = 0; i < this.labelInfo.length; i++) {
      PeriodAxisLabelInfo info = this.labelInfo[i];
      FontMetrics fm = g2.getFontMetrics(info.getLabelFont());
      tickLabelBandsDimension += info.getPadding().extendHeight(fm.getHeight());
    }

    if (RectangleEdge.isTopOrBottom(edge)) {
      labelHeight = labelEnclosure.getHeight();
      space.add(labelHeight + tickLabelBandsDimension, edge);
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      labelWidth = labelEnclosure.getWidth();
      space.add(labelWidth + tickLabelBandsDimension, edge);
    }

    // add space for the outer tick labels, if any...
    double tickMarkSpace = 0.0;
    if (isTickMarksVisible()) {
      tickMarkSpace = getTickMarkOutsideLength();
    }
    if (this.minorTickMarksVisible) {
      tickMarkSpace = Math.max(tickMarkSpace, this.minorTickMarkOutsideLength);
    }
    space.add(tickMarkSpace, edge);
    return space;
  }
コード例 #10
0
 public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) {
   Range range = getRange();
   double axisMin = range.getLowerBound();
   double axisMax = range.getUpperBound();
   double min = 0.0d;
   double max = 0.0d;
   if (RectangleEdge.isTopOrBottom(edge)) {
     min = area.getX();
     max = area.getMaxX();
   } else if (RectangleEdge.isLeftOrRight(edge)) {
     min = area.getMaxY();
     max = area.getY();
   }
   if (isInverted()) {
     return axisMax - (((java2DValue - min) / (max - min)) * (axisMax - axisMin));
   }
   return (((java2DValue - min) / (max - min)) * (axisMax - axisMin)) + axisMin;
 }
コード例 #11
0
  /**
   * Converts a coordinate in Java2D space to the corresponding data value, assuming that the axis
   * runs along one edge of the specified dataArea.
   *
   * @param java2DValue the coordinate in Java2D space.
   * @param area the area in which the data is plotted.
   * @param edge the edge along which the axis lies.
   * @return The data value.
   */
  public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) {

    double result = Double.NaN;
    double min = 0.0;
    double max = 0.0;
    double axisMin = this.first.getFirstMillisecond();
    double axisMax = this.last.getLastMillisecond();
    if (RectangleEdge.isTopOrBottom(edge)) {
      min = area.getX();
      max = area.getMaxX();
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      min = area.getMaxY();
      max = area.getY();
    }
    if (isInverted()) {
      result = axisMax - ((java2DValue - min) / (max - min) * (axisMax - axisMin));
    } else {
      result = axisMin + ((java2DValue - min) / (max - min) * (axisMax - axisMin));
    }
    return result;
  }
コード例 #12
0
ファイル: ColorBar.java プロジェクト: littlekai13/androidmcr
  /**
   * Draws the plot on a Java 2D graphics device (such as the screen or a printer).
   *
   * @param g2 the graphics device.
   * @param colorBarArea the area within which the axis should be drawn.
   * @param edge the location.
   */
  public void drawColorBar(Graphics2D g2, Rectangle2D colorBarArea, RectangleEdge edge) {

    Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

    // setTickValues was missing from ColorPalette v. 0.96
    // colorPalette.setTickValues(this.axis.getTicks());

    Stroke strokeSaved = g2.getStroke();
    g2.setStroke(new BasicStroke(1.0f));

    if (RectangleEdge.isTopOrBottom(edge)) {
      double y1 = colorBarArea.getY();
      double y2 = colorBarArea.getMaxY();
      double xx = colorBarArea.getX();
      Line2D line = new Line2D.Double();
      while (xx <= colorBarArea.getMaxX()) {
        double value = this.axis.java2DToValue(xx, colorBarArea, edge);
        line.setLine(xx, y1, xx, y2);
        g2.setPaint(getPaint(value));
        g2.draw(line);
        xx += 1;
      }
    } else {
      double y1 = colorBarArea.getX();
      double y2 = colorBarArea.getMaxX();
      double xx = colorBarArea.getY();
      Line2D line = new Line2D.Double();
      while (xx <= colorBarArea.getMaxY()) {
        double value = this.axis.java2DToValue(xx, colorBarArea, edge);
        line.setLine(y1, xx, y2, xx);
        g2.setPaint(getPaint(value));
        g2.draw(line);
        xx += 1;
      }
    }

    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
    g2.setStroke(strokeSaved);
  }
コード例 #13
0
ファイル: SubCategoryAxis.java プロジェクト: nologic/nabs
  /**
   * 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.
   */
  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;
    }

    space = super.reserveSpace(g2, plot, plotArea, edge, space);
    double maxdim = getMaxDim(g2, edge);
    if (RectangleEdge.isTopOrBottom(edge)) {
      space.add(maxdim, edge);
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      space.add(maxdim, edge);
    }
    return space;
  }
コード例 #14
0
  /**
   * Translates a value from data space to Java 2D space.
   *
   * @param value the data value.
   * @param dataArea the data area.
   * @param edge the edge.
   * @return The Java 2D value.
   */
  public double valueToJava2D(double value, Rectangle2D dataArea, RectangleEdge edge) {
    Range range = getRange();

    double vmin = range.getLowerBound();
    double vmax = range.getUpperBound();
    double vp = getCycleBound();

    if ((value < vmin) || (value > vmax)) {
      return Double.NaN;
    }

    double jmin = 0.0;
    double jmax = 0.0;
    if (RectangleEdge.isTopOrBottom(edge)) {
      jmin = dataArea.getMinX();
      jmax = dataArea.getMaxX();
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      jmax = dataArea.getMinY();
      jmin = dataArea.getMaxY();
    }

    if (isInverted()) {
      if (value == vp) {
        return this.boundMappedToLastCycle ? jmin : jmax;
      } else if (value > vp) {
        return jmax - (value - vp) * (jmax - jmin) / this.period;
      } else {
        return jmin + (vp - value) * (jmax - jmin) / this.period;
      }
    } else {
      if (value == vp) {
        return this.boundMappedToLastCycle ? jmax : jmin;
      } else if (value >= vp) {
        return jmin + (value - vp) * (jmax - jmin) / this.period;
      } else {
        return jmax - (vp - value) * (jmax - jmin) / this.period;
      }
    }
  }
コード例 #15
0
  /**
   * Converts a data value to a coordinate in Java2D space, assuming that the axis runs along one
   * edge of the specified dataArea.
   *
   * <p>Note that it is possible for the coordinate to fall outside the area.
   *
   * @param value the data value.
   * @param area the area for plotting the data.
   * @param edge the edge along which the axis lies.
   * @return The Java2D coordinate.
   */
  public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) {

    double result = Double.NaN;
    double axisMin = this.first.getFirstMillisecond();
    double axisMax = this.last.getLastMillisecond();
    if (RectangleEdge.isTopOrBottom(edge)) {
      double minX = area.getX();
      double maxX = area.getMaxX();
      if (isInverted()) {
        result = maxX + ((value - axisMin) / (axisMax - axisMin)) * (minX - maxX);
      } else {
        result = minX + ((value - axisMin) / (axisMax - axisMin)) * (maxX - minX);
      }
    } else if (RectangleEdge.isLeftOrRight(edge)) {
      double minY = area.getMinY();
      double maxY = area.getMaxY();
      if (isInverted()) {
        result = minY + (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY));
      } else {
        result = maxY - (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY));
      }
    }
    return result;
  }
コード例 #16
0
  /**
   * Draws the legend within the specified area.
   *
   * @param g2 the graphics target (<code>null</code> not permitted).
   * @param area the drawing area (<code>null</code> not permitted).
   * @param params drawing parameters (ignored here).
   * @return <code>null</code>.
   */
  public Object draw(Graphics2D g2, Rectangle2D area, Object params) {

    Rectangle2D target = (Rectangle2D) area.clone();
    target = trimMargin(target);
    if (this.backgroundPaint != null) {
      g2.setPaint(this.backgroundPaint);
      g2.fill(target);
    }
    getFrame().draw(g2, target);
    getFrame().getInsets().trim(target);
    target = trimPadding(target);
    double base = this.axis.getLowerBound();
    double increment = this.axis.getRange().getLength() / this.subdivisions;
    Rectangle2D r = new Rectangle2D.Double();

    if (RectangleEdge.isTopOrBottom(getPosition())) {
      RectangleEdge axisEdge =
          Plot.resolveRangeAxisLocation(this.axisLocation, PlotOrientation.HORIZONTAL);
      if (axisEdge == RectangleEdge.TOP) {
        for (int i = 0; i < this.subdivisions; i++) {
          double v = base + (i * increment);
          Paint p = this.scale.getPaint(v);
          double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.TOP);
          double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.TOP);
          double ww = Math.abs(vv1 - vv0) + 1.0;
          r.setRect(Math.min(vv0, vv1), target.getMaxY() - this.stripWidth, ww, this.stripWidth);
          g2.setPaint(p);
          g2.fill(r);
        }
        if (isStripOutlineVisible()) {
          g2.setPaint(this.stripOutlinePaint);
          g2.setStroke(this.stripOutlineStroke);
          g2.draw(
              new Rectangle2D.Double(
                  target.getMinX(),
                  target.getMaxY() - this.stripWidth,
                  target.getWidth(),
                  this.stripWidth));
        }
        this.axis.draw(
            g2,
            target.getMaxY() - this.stripWidth - this.axisOffset,
            target,
            target,
            RectangleEdge.TOP,
            null);
      } else if (axisEdge == RectangleEdge.BOTTOM) {
        for (int i = 0; i < this.subdivisions; i++) {
          double v = base + (i * increment);
          Paint p = this.scale.getPaint(v);
          double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.BOTTOM);
          double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.BOTTOM);
          double ww = Math.abs(vv1 - vv0) + 1.0;
          r.setRect(Math.min(vv0, vv1), target.getMinY(), ww, this.stripWidth);
          g2.setPaint(p);
          g2.fill(r);
        }
        if (isStripOutlineVisible()) {
          g2.setPaint(this.stripOutlinePaint);
          g2.setStroke(this.stripOutlineStroke);
          g2.draw(
              new Rectangle2D.Double(
                  target.getMinX(), target.getMinY(), target.getWidth(), this.stripWidth));
        }
        this.axis.draw(
            g2,
            target.getMinY() + this.stripWidth + this.axisOffset,
            target,
            target,
            RectangleEdge.BOTTOM,
            null);
      }
    } else {
      RectangleEdge axisEdge =
          Plot.resolveRangeAxisLocation(this.axisLocation, PlotOrientation.VERTICAL);
      if (axisEdge == RectangleEdge.LEFT) {
        for (int i = 0; i < this.subdivisions; i++) {
          double v = base + (i * increment);
          Paint p = this.scale.getPaint(v);
          double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.LEFT);
          double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.LEFT);
          double hh = Math.abs(vv1 - vv0) + 1.0;
          r.setRect(target.getMaxX() - this.stripWidth, Math.min(vv0, vv1), this.stripWidth, hh);
          g2.setPaint(p);
          g2.fill(r);
        }
        if (isStripOutlineVisible()) {
          g2.setPaint(this.stripOutlinePaint);
          g2.setStroke(this.stripOutlineStroke);
          g2.draw(
              new Rectangle2D.Double(
                  target.getMaxX() - this.stripWidth,
                  target.getMinY(),
                  this.stripWidth,
                  target.getHeight()));
        }
        this.axis.draw(
            g2,
            target.getMaxX() - this.stripWidth - this.axisOffset,
            target,
            target,
            RectangleEdge.LEFT,
            null);
      } else if (axisEdge == RectangleEdge.RIGHT) {
        for (int i = 0; i < this.subdivisions; i++) {
          double v = base + (i * increment);
          Paint p = this.scale.getPaint(v);
          double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.LEFT);
          double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.LEFT);
          double hh = Math.abs(vv1 - vv0) + 1.0;
          r.setRect(target.getMinX(), Math.min(vv0, vv1), this.stripWidth, hh);
          g2.setPaint(p);
          g2.fill(r);
        }
        if (isStripOutlineVisible()) {
          g2.setPaint(this.stripOutlinePaint);
          g2.setStroke(this.stripOutlineStroke);
          g2.draw(
              new Rectangle2D.Double(
                  target.getMinX(), target.getMinY(), this.stripWidth, target.getHeight()));
        }
        this.axis.draw(
            g2,
            target.getMinX() + this.stripWidth + this.axisOffset,
            target,
            target,
            RectangleEdge.RIGHT,
            null);
      }
    }
    return null;
  }
コード例 #17
0
ファイル: SubCategoryAxis.java プロジェクト: nologic/nabs
  /**
   * 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;
  }