Package com.positive.charts.axis

Source Code of com.positive.charts.axis.ValueAxis

package com.positive.charts.axis;

import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.List;

import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

import com.positive.charts.axis.ticks.ITick;
import com.positive.charts.axis.ticks.TickUnitSource;
import com.positive.charts.axis.ticks.ValueTick;
import com.positive.charts.block.RectangleInsets;
import com.positive.charts.common.RectangleEdge;
import com.positive.charts.data.Range;
import com.positive.charts.event.AxisChangeEvent;
import com.positive.charts.plot.Plot;
import com.positive.charts.util.TextUtilities;

/**
* The base class for axes that display value data, where values are measured
* using the <code>double</code> primitive. The two key subclasses are
* {@link DateAxis} and {@link NumberAxis}.
*/
public abstract class ValueAxis extends BaseAxis {

  /** The default axis range. */
  public static final Range DEFAULT_RANGE = new Range(0.0, 1.0);

  /** The default auto-range value. */
  public static final boolean DEFAULT_AUTO_RANGE = true;

  /** The default inverted flag setting. */
  public static final boolean DEFAULT_INVERTED = false;

  /** The default minimum auto range. */
  public static final double DEFAULT_AUTO_RANGE_MINIMUM_SIZE = 0.00000001;

  /** The default value for the lower margin (0.05 = 5%). */
  public static final double DEFAULT_LOWER_MARGIN = 0.05;

  /** The default value for the upper margin (0.05 = 5%). */
  public static final double DEFAULT_UPPER_MARGIN = 0.05;

  /** The default lower bound for the axis. */
  public static final double DEFAULT_LOWER_BOUND = 0.0;

  /** The default upper bound for the axis. */
  public static final double DEFAULT_UPPER_BOUND = 1.0;

  /** The default auto-tick-unit-selection value. */
  public static final boolean DEFAULT_AUTO_TICK_UNIT_SELECTION = true;

  /** The maximum tick count. */
  public static final int MAXIMUM_TICK_COUNT = 500;

  /**
   * A flag that controls whether an arrow is drawn at the positive end of the
   * axis line.
   */
  private boolean positiveArrowVisible;

  /**
   * A flag that controls whether an arrow is drawn at the negative end of the
   * axis line.
   */
  private boolean negativeArrowVisible;

  /** A flag that affects the orientation of the values on the axis. */
  private boolean inverted;

  /** The axis range. */
  private Range range;

  /**
   * Flag that indicates whether the axis automatically scales to fit the
   * chart data.
   */
  private boolean autoRange;

  /** The minimum size for the 'auto' axis range (excluding margins). */
  private double autoRangeMinimumSize;

  /**
   * The upper margin percentage. This indicates the amount by which the
   * maximum axis value exceeds the maximum data value (as a percentage of the
   * range on the axis) when the axis range is determined automatically.
   */
  private double upperMargin;

  /**
   * The lower margin. This is a percentage that indicates the amount by which
   * the minimum axis value is "less than" the minimum data value when the
   * axis range is determined automatically.
   */
  private double lowerMargin;

  /**
   * If this value is positive, the amount is subtracted from the maximum data
   * value to determine the lower axis range. This can be used to provide a
   * fixed "window" on dynamic data.
   */
  private double fixedAutoRange;

  /**
   * Flag that indicates whether or not the tick unit is selected
   * automatically.
   */
  private boolean autoTickUnitSelection;

  /** The standard tick units for the axis. */
  private TickUnitSource standardTickUnits;

  /** An index into an array of standard tick values. */
  private int autoTickIndex;

  /** A flag indicating whether or not tick labels are rotated to vertical. */
  private boolean verticalTickLabels;

  /**
   * Constructs a value axis.
   *
   * @param label
   *            the axis label.
   * @param standardTickUnits
   *            the source for standard tick units (<code>null</code>
   *            permitted).
   */
  protected ValueAxis(final String label,
      final TickUnitSource standardTickUnits) {

    super(label);

    this.positiveArrowVisible = false;
    this.negativeArrowVisible = false;

    this.range = DEFAULT_RANGE;
    this.autoRange = DEFAULT_AUTO_RANGE;

    this.inverted = DEFAULT_INVERTED;
    this.autoRangeMinimumSize = DEFAULT_AUTO_RANGE_MINIMUM_SIZE;

    this.lowerMargin = DEFAULT_LOWER_MARGIN;
    this.upperMargin = DEFAULT_UPPER_MARGIN;

    this.fixedAutoRange = 0.0;

    this.autoTickUnitSelection = DEFAULT_AUTO_TICK_UNIT_SELECTION;
    this.standardTickUnits = standardTickUnits;

    this.verticalTickLabels = false;

  }

  /**
   * Automatically sets the axis range to fit the range of values in the
   * dataset. Sometimes this can depend on the renderer used as well (for
   * example, the renderer may "stack" values, requiring an axis range greater
   * than otherwise necessary).
   */
  protected abstract void autoAdjustRange();

  /**
   * Calculates the anchor point for a tick label.
   *
   * @param tick
   *            the tick.
   * @param cursor
   *            the cursor.
   * @param dataArea
   *            the data area.
   * @param edge
   *            the edge on which the axis is drawn.
   *
   * @return The anchor point.
   */
  protected Point calculateAnchorPoint(final ValueTick tick,
      final double cursor, final Rectangle dataArea,
      final RectangleEdge edge) {
    final RectangleInsets insets = this.getTickLabelInsets();
    // FIXME Convert these routines to use integers
    final float[] result = new float[2];

    if (edge == RectangleEdge.TOP) {
      result[0] = (float) this.valueToJava2D(tick.getValue(), dataArea,
          edge);
      result[1] = (float) (cursor - insets.getBottom() - 2.0);
    } else if (edge == RectangleEdge.BOTTOM) {
      result[0] = (float) this.valueToJava2D(tick.getValue(), dataArea,
          edge);
      result[1] = (float) (cursor + insets.getTop() + 2.0);
    } else if (edge == RectangleEdge.LEFT) {
      result[0] = (float) (cursor - insets.getLeft() - 2.0);
      result[1] = (float) this.valueToJava2D(tick.getValue(), dataArea,
          edge);
    } else if (edge == RectangleEdge.RIGHT) {
      result[0] = (float) (cursor + insets.getRight() + 2.0);
      result[1] = (float) this.valueToJava2D(tick.getValue(), dataArea,
          edge);
    }
    return new Point((int) result[0], (int) result[1]);
  }

  /**
   * Centers the axis range about the specified value and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param value
   *            the center value.
   */
  public void centerRange(final double value) {

    final double central = this.range.getCentralValue();
    final Range adjusted = new Range(this.range.getLowerBound() + value
        - central, this.range.getUpperBound() + value - central);
    this.setRange(adjusted);

  }

  /**
   * Draws an axis line at the current cursor position and edge.
   *
   * @param g2
   *            the graphics device.
   * @param cursor
   *            the cursor position. TODO Use integers
   * @param dataArea
   *            the data area.
   * @param edge
   *            the edge.
   */
  protected void drawAxisLine(final GC g2, final double cursor,
      final Rectangle dataArea, final RectangleEdge edge) {

    // TODO Set line style and color
    // g2.setPaint(getAxisLinePaint());
    // g2.setStroke(getAxisLineStroke());

    if (edge.isTopOrBottom()) {
      g2.drawLine(dataArea.x, (int) cursor, dataArea.x + dataArea.width,
          (int) cursor);
    } else {
      g2.drawLine((int) cursor, dataArea.y, (int) cursor, dataArea.y
          + dataArea.height);
    }

    boolean drawUpOrRight = false;
    boolean drawDownOrLeft = false;
    if (this.positiveArrowVisible) {
      if (this.inverted) {
        drawDownOrLeft = true;
      } else {
        drawUpOrRight = true;
      }
    }
    if (this.negativeArrowVisible) {
      if (this.inverted) {
        drawUpOrRight = true;
      } else {
        drawDownOrLeft = true;
      }
    }

    if (drawUpOrRight) {
      // double x = 0.0;
      // double y = 0.0;
      // Shape arrow = null;
      // if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
      // x = dataArea.getMaxX();
      // y = cursor;
      // arrow = this.rightArrow;
      // } else if (edge == RectangleEdge.LEFT
      // || edge == RectangleEdge.RIGHT) {
      // x = cursor;
      // y = dataArea.getMinY();
      // arrow = this.upArrow;
      // }
      //
      // // draw the arrow...
      // AffineTransform transformer = new AffineTransform();
      // transformer.setToTranslation(x, y);
      // Shape shape = transformer.createTransformedShape(arrow);
      // g2.fill(shape);
      // g2.draw(shape);
    }

    if (drawDownOrLeft) {
      // double x = 0.0;
      // double y = 0.0;
      // Shape arrow = null;
      // if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
      // x = dataArea.getMinX();
      // y = cursor;
      // arrow = this.leftArrow;
      // } else if (edge == RectangleEdge.LEFT
      // || edge == RectangleEdge.RIGHT) {
      // x = cursor;
      // y = dataArea.getMaxY();
      // arrow = this.downArrow;
      // }
      //
      // // draw the arrow...
      // AffineTransform transformer = new AffineTransform();
      // transformer.setToTranslation(x, y);
      // Shape shape = transformer.createTransformedShape(arrow);
      // g2.fill(shape);
      // g2.draw(shape);
    }

  }

  /**
   * Draws the axis line, tick marks and tick mark labels.
   *
   * @param g2
   *            the graphics device.
   * @param cursor
   *            the cursor.
   * @param plotArea
   *            the plot area.
   * @param dataArea
   *            the data area.
   * @param edge
   *            the edge that the axis is aligned with.
   *
   * @return The width or height used to draw the axis.
   */
  protected AxisState drawTickMarksAndLabels(final GC g2,
      final double cursor, final Rectangle plotArea,
      final Rectangle dataArea, final RectangleEdge edge) {

    final AxisState state = new AxisState(cursor);

    if (this.isAxisLineVisible()) {
      this.drawAxisLine(g2, cursor, dataArea, edge);
    }

    final double ol = this.getTickMarkOutsideLength();
    final double il = this.getTickMarkInsideLength();

    final List ticks = this.refreshTicks(g2, state, dataArea, edge);
    state.setTicks(ticks);
    g2.setFont(this.getTickLabelFont());
    final Iterator iterator = ticks.iterator();
    while (iterator.hasNext()) {
      final ValueTick tick = (ValueTick) iterator.next();
      if (this.isTickLabelsVisible()) {
        // TODO Set color
        // g2.setPaint(getTickLabelPaint());
        final Point anchorPoint = this.calculateAnchorPoint(tick,
            cursor, dataArea, edge);
        TextUtilities.drawRotatedString(tick.getText(), g2,
            anchorPoint.x, anchorPoint.y, tick.getTextAnchor(),
            tick.getAngle(), tick.getRotationAnchor());
      }

      if (this.isTickMarksVisible()) {
        final int xx = (int) this.valueToJava2D(tick.getValue(),
            dataArea, edge);

        // TODO Set color and linestyle
        // g2.setStroke(getTickMarkStroke());
        // g2.setPaint(getTickMarkPaint());

        // TODO Use integers
        if (edge == RectangleEdge.LEFT) {
          g2.drawLine((int) (cursor - ol), xx, (int) (cursor + il),
              xx);
        } else if (edge == RectangleEdge.RIGHT) {
          g2.drawLine((int) (cursor + ol), xx, (int) (cursor - il),
              xx);
        } else if (edge == RectangleEdge.TOP) {
          g2.drawLine(xx, (int) (cursor - ol), xx,
              (int) (cursor + il));
        } else if (edge == RectangleEdge.BOTTOM) {
          g2.drawLine(xx, (int) (cursor + ol), xx,
              (int) (cursor - il));
        }
      }
    }

    // need to work out the space used by the tick labels...
    // so we can update the cursor...
    double used = 0.0;
    if (this.isTickLabelsVisible()) {
      if (edge == RectangleEdge.LEFT) {
        used += this.findMaximumTickLabelWidth(ticks, g2, plotArea,
            this.isVerticalTickLabels());
        state.cursorLeft(used);
      } else if (edge == RectangleEdge.RIGHT) {
        used = this.findMaximumTickLabelWidth(ticks, g2, plotArea, this
            .isVerticalTickLabels());
        state.cursorRight(used);
      } else if (edge == RectangleEdge.TOP) {
        used = this.findMaximumTickLabelHeight(ticks, g2, plotArea,
            this.isVerticalTickLabels());
        state.cursorUp(used);
      } else if (edge == RectangleEdge.BOTTOM) {
        used = this.findMaximumTickLabelHeight(ticks, g2, plotArea,
            this.isVerticalTickLabels());
        state.cursorDown(used);
      }
    }

    return state;
  }

  /**
   * A utility method for determining the height of the tallest tick label.
   *
   * @param ticks
   *            the ticks.
   * @param g2
   *            the graphics device.
   * @param drawArea
   *            the area within which the plot and axes should be drawn.
   * @param vertical
   *            a flag that indicates whether or not the tick labels are
   *            'vertical'.
   *
   * @return The height of the tallest tick label.
   */
  protected int findMaximumTickLabelHeight(final List ticks, final GC g2,
      final Rectangle drawArea, final boolean vertical) {
    final RectangleInsets insets = this.getTickLabelInsets();
    final Font font = this.getTickLabelFont();
    int maxHeight = 0;
    g2.setFont(font);
    final FontMetrics fm = g2.getFontMetrics();
    if (vertical) {
      for (final Iterator iter = ticks.iterator(); iter.hasNext();) {
        final ITick tick = (ITick) iter.next();
        final Point extent = g2.textExtent(tick.getText());
        maxHeight = Math.max(maxHeight, extent.x + insets.getTop()
            + insets.getBottom());
      }
    } else {
      maxHeight = fm.getHeight() + insets.getTop() + insets.getBottom();
    }
    return maxHeight;

  }

  /**
   * A utility method for determining the width of the widest tick label.
   *
   * @param ticks
   *            the ticks.
   * @param g2
   *            the graphics device.
   * @param drawArea
   *            the area within which the plot and axes should be drawn.
   * @param vertical
   *            a flag that indicates whether or not the tick labels are
   *            'vertical'.
   *
   * @return The width of the tallest tick label.
   */
  protected int findMaximumTickLabelWidth(final List ticks, final GC g2,
      final Rectangle drawArea, final boolean vertical) {
    final RectangleInsets insets = this.getTickLabelInsets();
    final Font font = this.getTickLabelFont();
    int maxWidth = 0;
    g2.setFont(font);
    final FontMetrics fm = g2.getFontMetrics();
    if (!vertical) {
      for (final Iterator iter = ticks.iterator(); iter.hasNext();) {
        final ITick tick = (ITick) iter.next();
        final Point extent = g2.textExtent(tick.getText());
        maxWidth = Math.max(maxWidth, extent.x + insets.getLeft()
            + insets.getRight());
      }
    } else {
      maxWidth = fm.getHeight() + insets.getTop() + insets.getBottom();
    }
    return maxWidth;

  }

  /**
   * Returns the minimum size allowed for the axis range when it is
   * automatically calculated.
   *
   * @return The minimum range.
   *
   * @see #setAutoRangeMinimumSize(double)
   */
  public double getAutoRangeMinimumSize() {
    return this.autoRangeMinimumSize;
  }

  /**
   * Returns the auto tick index.
   *
   * @return The auto tick index.
   *
   * @see #setAutoTickIndex(int)
   */
  protected int getAutoTickIndex() {
    return this.autoTickIndex;
  }

  /**
   * Returns the fixed auto range.
   *
   * @return The length.
   *
   * @see #setFixedAutoRange(double)
   */
  public double getFixedAutoRange() {
    return this.fixedAutoRange;
  }

  /**
   * Returns the lower bound of the axis range.
   *
   * @return The lower bound.
   *
   * @see #setLowerBound(double)
   */
  public double getLowerBound() {
    return this.range.getLowerBound();
  }

  /**
   * Returns the lower margin for the axis, expressed as a percentage of the
   * axis range. This controls the space added to the lower end of the axis
   * when the axis range is automatically calculated (it is ignored when the
   * axis range is set explicitly). The default value is 0.05 (five percent).
   *
   * @return The lower margin.
   *
   * @see #setLowerMargin(double)
   */
  public double getLowerMargin() {
    return this.lowerMargin;
  }

  /**
   * Returns the range for the axis.
   *
   * @return The axis range (never <code>null</code>).
   *
   * @see #setRange(Range)
   */
  public Range getRange() {
    return this.range;
  }

  /**
   * Returns the source for obtaining standard tick units for the axis.
   *
   * @return The source (possibly <code>null</code>).
   *
   * @see #setStandardTickUnits(TickUnitSource)
   */
  public TickUnitSource getStandardTickUnits() {
    return this.standardTickUnits;
  }

  /**
   * Returns the upper bound for the axis range.
   *
   * @return The upper bound.
   *
   * @see #setUpperBound(double)
   */
  public double getUpperBound() {
    return this.range.getUpperBound();
  }

  /**
   * Returns the upper margin for the axis, expressed as a percentage of the
   * axis range. This controls the space added to the lower end of the axis
   * when the axis range is automatically calculated (it is ignored when the
   * axis range is set explicitly). The default value is 0.05 (five percent).
   *
   * @return The upper margin.
   *
   * @see #setUpperMargin(double)
   */
  public double getUpperMargin() {
    return this.upperMargin;
  }

  /**
   * Returns the flag that controls whether or not the axis range is
   * automatically adjusted to fit the data values.
   *
   * @return The flag.
   *
   * @see #setAutoRange(boolean)
   */
  public boolean isAutoRange() {
    return this.autoRange;
  }

  /**
   * Returns a flag indicating whether or not the tick unit is automatically
   * selected from a range of standard tick units.
   *
   * @return A flag indicating whether or not the tick unit is automatically
   *         selected.
   *
   * @see #setAutoTickUnitSelection(boolean)
   */
  public boolean isAutoTickUnitSelection() {
    return this.autoTickUnitSelection;
  }

  /**
   * Returns a flag that controls the direction of values on the axis.
   * <P>
   * For a regular axis, values increase from left to right (for a horizontal
   * axis) and bottom to top (for a vertical axis). When the axis is
   * 'inverted', the values increase in the opposite direction.
   *
   * @return The flag.
   *
   * @see #setInverted(boolean)
   */
  public boolean isInverted() {
    return this.inverted;
  }

  /**
   * Returns a flag that controls whether or not the axis line has an arrow
   * drawn that points in the negative direction for the axis.
   *
   * @return A boolean.
   *
   * @see #setNegativeArrowVisible(boolean)
   */
  public boolean isNegativeArrowVisible() {
    return this.negativeArrowVisible;
  }

  /**
   * Returns a flag that controls whether or not the axis line has an arrow
   * drawn that points in the positive direction for the axis.
   *
   * @return A boolean.
   *
   * @see #setPositiveArrowVisible(boolean)
   */
  public boolean isPositiveArrowVisible() {
    return this.positiveArrowVisible;
  }

  /**
   * Returns <code>true</code> if the tick labels should be rotated (to
   * vertical), and <code>false</code> otherwise.
   *
   * @return <code>true</code> or <code>false</code>.
   *
   * @see #setVerticalTickLabels(boolean)
   */
  public boolean isVerticalTickLabels() {
    return this.verticalTickLabels;
  }

  /**
   * 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.
   *
   * @see #valueToJava2D(double, Rectangle2D, RectangleEdge)
   */
  public abstract double java2DToValue(double java2DValue, Rectangle area,
      RectangleEdge edge);

  /**
   * Converts a length in data coordinates into the corresponding length in
   * Java2D coordinates.
   *
   * @param length
   *            the length.
   * @param area
   *            the plot area.
   * @param edge
   *            the edge along which the axis lies.
   *
   * @return The length in Java2D coordinates.
   */
  public double lengthToJava2D(final double length, final Rectangle area,
      final RectangleEdge edge) {
    final double zero = this.valueToJava2D(0.0, area, edge);
    final double l = this.valueToJava2D(length, area, edge);
    return Math.abs(l - zero);
  }

  /**
   * Returns the space 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 should be drawn.
   * @param edge
   *            the axis location.
   * @param space
   *            the space already reserved (for other axes).
   *
   * @return The space required to draw the axis (including pre-reserved
   *         space).
   */
  public AxisSpace reserveSpace(final GC g2, final Plot plot,
      final Rectangle plotArea, final 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 (!this.isVisible()) {
      return space;
    }

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

    // calculate the max size of the tick labels (if visible)...
    int tickLabelHeight = 0;
    int tickLabelWidth = 0;
    if (this.isTickLabelsVisible()) {
      g2.setFont(this.getTickLabelFont());
      final List ticks = this.refreshTicks(g2, new AxisState(), plotArea,
          edge);
      if (edge.isTopOrBottom()) {
        tickLabelHeight = this.findMaximumTickLabelHeight(ticks, g2,
            plotArea, this.isVerticalTickLabels());
      } else if (edge.isLeftOrRight()) {
        tickLabelWidth = this.findMaximumTickLabelWidth(ticks, g2,
            plotArea, this.isVerticalTickLabels());
      }
    }

    // get the axis label size and update the space object...
    final Rectangle labelEnclosure = this.getLabelEnclosure(g2, edge);
    int labelHeight = 0;
    int labelWidth = 0;
    if (edge.isTopOrBottom()) {
      labelHeight = labelEnclosure.height;
      space.add(labelHeight + tickLabelHeight, edge);
    } else if (edge.isLeftOrRight()) {
      labelWidth = labelEnclosure.width;
      space.add(labelWidth + tickLabelWidth, edge);
    }

    return space;

  }

  /**
   * Increases or decreases the axis range by the specified percentage about
   * the central value and sends an {@link AxisChangeEvent} to all registered
   * listeners.
   * <P>
   * To double the length of the axis range, use 200% (2.0). To halve the
   * length of the axis range, use 50% (0.5).
   *
   * @param percent
   *            the resize factor.
   */
  public void resizeRange(final double percent) {
    this.resizeRange(percent, this.range.getCentralValue());
  }

  /**
   * Increases or decreases the axis range by the specified percentage about
   * the specified anchor value and sends an {@link AxisChangeEvent} to all
   * registered listeners.
   * <P>
   * To double the length of the axis range, use 200% (2.0). To halve the
   * length of the axis range, use 50% (0.5).
   *
   * @param percent
   *            the resize factor.
   * @param anchorValue
   *            the new central value after the resize.
   */
  public void resizeRange(final double percent, final double anchorValue) {
    if (percent > 0.0) {
      final double halfLength = this.range.getLength() * percent / 2;
      final Range adjusted = new Range(anchorValue - halfLength,
          anchorValue + halfLength);
      this.setRange(adjusted);
    } else {
      this.setAutoRange(true);
    }
  }

  /**
   * Sets a flag that determines whether or not the axis range is
   * automatically adjusted to fit the data, and notifies registered listeners
   * that the axis has been modified.
   *
   * @param auto
   *            the new value of the flag.
   *
   * @see #isAutoRange()
   */
  public void setAutoRange(final boolean auto) {
    this.setAutoRange(auto, true);
  }

  /**
   * Sets the auto range attribute. If the <code>notify</code> flag is set, an
   * {@link AxisChangeEvent} is sent to registered listeners.
   *
   * @param auto
   *            the flag.
   * @param notify
   *            notify listeners?
   *
   * @see #isAutoRange()
   */
  protected void setAutoRange(final boolean auto, final boolean notify) {
    if (this.autoRange != auto) {
      this.autoRange = auto;
      if (this.autoRange) {
        this.autoAdjustRange();
      }
      if (notify) {
        this.notifyListeners(new AxisChangeEvent(this));
      }
    }
  }

  /**
   * Sets the auto range minimum size and sends an {@link AxisChangeEvent} to
   * all registered listeners.
   *
   * @param size
   *            the size.
   *
   * @see #getAutoRangeMinimumSize()
   */
  public void setAutoRangeMinimumSize(final double size) {
    this.setAutoRangeMinimumSize(size, true);
  }

  /**
   * Sets the minimum size allowed for the axis range when it is automatically
   * calculated.
   * <p>
   * If requested, an {@link AxisChangeEvent} is forwarded to all registered
   * listeners.
   *
   * @param size
   *            the new minimum.
   * @param notify
   *            notify listeners?
   */
  public void setAutoRangeMinimumSize(final double size, final boolean notify) {
    if (size <= 0.0) {
      throw new IllegalArgumentException(
          "NumberAxis.setAutoRangeMinimumSize(double): must be > 0.0.");
    }
    if (this.autoRangeMinimumSize != size) {
      this.autoRangeMinimumSize = size;
      if (this.autoRange) {
        this.autoAdjustRange();
      }
      if (notify) {
        this.notifyListeners(new AxisChangeEvent(this));
      }
    }

  }

  /**
   * Sets the auto tick index.
   *
   * @param index
   *            the new value.
   *
   * @see #getAutoTickIndex()
   */
  protected void setAutoTickIndex(final int index) {
    this.autoTickIndex = index;
  }

  /**
   * Sets a flag indicating whether or not the tick unit is automatically
   * selected from a range of standard tick units. If the flag is changed,
   * registered listeners are notified that the chart has changed.
   *
   * @param flag
   *            the new value of the flag.
   *
   * @see #isAutoTickUnitSelection()
   */
  public void setAutoTickUnitSelection(final boolean flag) {
    this.setAutoTickUnitSelection(flag, true);
  }

  /**
   * Sets a flag indicating whether or not the tick unit is automatically
   * selected from a range of standard tick units.
   *
   * @param flag
   *            the new value of the flag.
   * @param notify
   *            notify listeners?
   *
   * @see #isAutoTickUnitSelection()
   */
  public void setAutoTickUnitSelection(final boolean flag,
      final boolean notify) {

    if (this.autoTickUnitSelection != flag) {
      this.autoTickUnitSelection = flag;
      if (notify) {
        this.notifyListeners(new AxisChangeEvent(this));
      }
    }
  }

  /**
   * Sets the fixed auto range for the axis.
   *
   * @param length
   *            the range length.
   *
   * @see #getFixedAutoRange()
   */
  public void setFixedAutoRange(final double length) {
    this.fixedAutoRange = length;
    if (this.isAutoRange()) {
      this.autoAdjustRange();
    }
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets a flag that controls the direction of values on the axis, and
   * notifies registered listeners that the axis has changed.
   *
   * @param flag
   *            the flag.
   *
   * @see #isInverted()
   */
  public void setInverted(final boolean flag) {

    if (this.inverted != flag) {
      this.inverted = flag;
      this.notifyListeners(new AxisChangeEvent(this));
    }

  }

  /**
   * Sets the lower bound for the axis range. An {@link AxisChangeEvent} is
   * sent to all registered listeners.
   *
   * @param min
   *            the new minimum.
   *
   * @see #getLowerBound()
   */
  public void setLowerBound(final double min) {
    if (this.range.getUpperBound() > min) {
      this.setRange(new Range(min, this.range.getUpperBound()));
    } else {
      this.setRange(new Range(min, min + 1.0));
    }
  }

  /**
   * Sets the lower margin for the axis (as a percentage of the axis range)
   * and sends an {@link AxisChangeEvent} to all registered listeners. This
   * margin is added only when the axis range is auto-calculated - if you set
   * the axis range manually, the margin is ignored.
   *
   * @param margin
   *            the margin percentage (for example, 0.05 is five percent).
   *
   * @see #setUpperMargin(double)
   */
  public void setLowerMargin(final double margin) {
    this.lowerMargin = margin;
    if (this.isAutoRange()) {
      this.autoAdjustRange();
    }
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets a flag that controls whether or not the axis lines has an arrow
   * drawn that points in the negative direction for the axis, and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param visible
   *            the flag.
   *
   * @see #setNegativeArrowVisible(boolean)
   */
  public void setNegativeArrowVisible(final boolean visible) {
    this.negativeArrowVisible = visible;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets a flag that controls whether or not the axis lines has an arrow
   * drawn that points in the positive direction for the axis, and sends an
   * {@link AxisChangeEvent} to all registered listeners.
   *
   * @param visible
   *            the flag.
   *
   * @see #isPositiveArrowVisible()
   */
  public void setPositiveArrowVisible(final boolean visible) {
    this.positiveArrowVisible = visible;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the axis range and sends an {@link AxisChangeEvent} to all
   * registered listeners. As a side-effect, the auto-range flag is set to
   * <code>false</code>.
   *
   * @param lower
   *            the lower axis limit.
   * @param upper
   *            the upper axis limit.
   *
   * @see #getRange()
   */
  public void setRange(final double lower, final double upper) {
    this.setRange(new Range(lower, upper));
  }

  /**
   * Sets the range attribute and sends an {@link AxisChangeEvent} to all
   * registered listeners. As a side-effect, the auto-range flag is set to
   * <code>false</code>.
   *
   * @param range
   *            the range (<code>null</code> not permitted).
   *
   * @see #getRange()
   */
  public void setRange(final Range range) {
    // defer argument checking
    this.setRange(range, true, true);
  }

  /**
   * Sets the range for the axis, if requested, sends an
   * {@link AxisChangeEvent} to all registered listeners. As a side-effect,
   * the auto-range flag is set to <code>false</code> (optional).
   *
   * @param range
   *            the range (<code>null</code> not permitted).
   * @param turnOffAutoRange
   *            a flag that controls whether or not the auto range is turned
   *            off.
   * @param notify
   *            a flag that controls whether or not listeners are notified.
   *
   * @see #getRange()
   */
  public void setRange(final Range range, final boolean turnOffAutoRange,
      final boolean notify) {
    if (range == null) {
      throw new IllegalArgumentException("Null 'range' argument.");
    }
    if (turnOffAutoRange) {
      this.autoRange = false;
    }
    this.range = range;
    if (notify) {
      this.notifyListeners(new AxisChangeEvent(this));
    }
  }

  /**
   * Sets the axis range, where the new range is 'size' in length, and
   * centered on 'value'.
   *
   * @param value
   *            the central value.
   * @param length
   *            the range length.
   */
  public void setRangeAboutValue(final double value, final double length) {
    this.setRange(new Range(value - length / 2, value + length / 2));
  }

  /**
   * Sets the axis range (after first adding the current margins to the range)
   * and sends an {@link AxisChangeEvent} to all registered listeners. As a
   * side-effect, the auto-range flag is set to <code>false</code>.
   *
   * @param lower
   *            the lower axis limit.
   * @param upper
   *            the upper axis limit.
   */
  public void setRangeWithMargins(final double lower, final double upper) {
    this.setRangeWithMargins(new Range(lower, upper));
  }

  /**
   * Sets the range for the axis (after first adding the current margins to
   * the specified range) and sends an {@link AxisChangeEvent} to all
   * registered listeners.
   *
   * @param range
   *            the range (<code>null</code> not permitted).
   */
  public void setRangeWithMargins(final Range range) {
    this.setRangeWithMargins(range, true, true);
  }

  /**
   * Sets the range for the axis after first adding the current margins to the
   * range and, if requested, sends an {@link AxisChangeEvent} to all
   * registered listeners. As a side-effect, the auto-range flag is set to
   * <code>false</code> (optional).
   *
   * @param range
   *            the range (excluding margins, <code>null</code> not
   *            permitted).
   * @param turnOffAutoRange
   *            a flag that controls whether or not the auto range is turned
   *            off.
   * @param notify
   *            a flag that controls whether or not listeners are notified.
   */
  public void setRangeWithMargins(final Range range,
      final boolean turnOffAutoRange, final boolean notify) {
    if (range == null) {
      throw new IllegalArgumentException("Null 'range' argument.");
    }
    this.setRange(Range.expand(range, this.getLowerMargin(), this
        .getUpperMargin()), turnOffAutoRange, notify);
  }

  /**
   * Sets the source for obtaining standard tick units for the axis and sends
   * an {@link AxisChangeEvent} to all registered listeners. The axis will try
   * to select the smallest tick unit from the source that does not cause the
   * tick labels to overlap (see also the
   * {@link #setAutoTickUnitSelection(boolean)} method.
   *
   * @param source
   *            the source for standard tick units (<code>null</code>
   *            permitted).
   *
   * @see #getStandardTickUnits()
   */
  public void setStandardTickUnits(final TickUnitSource source) {
    this.standardTickUnits = source;
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the upper bound for the axis range. An {@link AxisChangeEvent} is
   * sent to all registered listeners.
   *
   * @param max
   *            the new maximum.
   *
   * @see #getUpperBound()
   */
  public void setUpperBound(final double max) {

    if (this.range.getLowerBound() < max) {
      this.setRange(new Range(this.range.getLowerBound(), max));
    } else {
      this.setRange(max - 1.0, max);
    }

  }

  /**
   * Sets the upper margin for the axis (as a percentage of the axis range)
   * and sends an {@link AxisChangeEvent} to all registered listeners. This
   * margin is added only when the axis range is auto-calculated - if you set
   * the axis range manually, the margin is ignored.
   *
   * @param margin
   *            the margin percentage (for example, 0.05 is five percent).
   *
   * @see #setLowerMargin(double)
   */
  public void setUpperMargin(final double margin) {
    this.upperMargin = margin;
    if (this.isAutoRange()) {
      this.autoAdjustRange();
    }
    this.notifyListeners(new AxisChangeEvent(this));
  }

  /**
   * Sets the flag that controls whether the tick labels are displayed
   * vertically (that is, rotated 90 degrees from horizontal). If the flag is
   * changed, an {@link AxisChangeEvent} is sent to all registered listeners.
   *
   * @param flag
   *            the flag.
   *
   * @see #isVerticalTickLabels()
   */
  public void setVerticalTickLabels(final boolean flag) {
    if (this.verticalTickLabels != flag) {
      this.verticalTickLabels = flag;
      this.notifyListeners(new AxisChangeEvent(this));
    }
  }

  /**
   * 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.
   *
   * @see #java2DToValue(double, Rectangle2D, RectangleEdge)
   */
  public abstract double valueToJava2D(double value, Rectangle area,
      RectangleEdge edge);

  /**
   * Zooms in on the current range.
   *
   * @param lowerPercent
   *            the new lower bound.
   * @param upperPercent
   *            the new upper bound.
   */
  public void zoomRange(final double lowerPercent, final double upperPercent) {
    final double start = this.range.getLowerBound();
    final double length = this.range.getLength();
    Range adjusted = null;
    if (this.isInverted()) {
      adjusted = new Range(start + (length * (1 - upperPercent)), start
          + (length * (1 - lowerPercent)));
    } else {
      adjusted = new Range(start + length * lowerPercent, start + length
          * upperPercent);
    }
    this.setRange(adjusted);
  }

}
 
TOP

Related Classes of com.positive.charts.axis.ValueAxis

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