/*
* AxisScalePolicyAutomaticBestFit.java of project jchart2d, <enterpurposehere>.
* Copyright (C) 2002 - 2011, Achim Westermann, created on Apr 22, 2011
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* If you modify or optimize the code in a useful way please let me know.
* Achim.Westermann@gmx.de
*
*
* File : $Source: /cvsroot/jchart2d/jchart2d/codetemplates.xml,v $
* Date : $Date: 2009/02/24 16:45:41 $
* Version: $Revision: 1.2 $
*/
package info.monitorenter.gui.chart.axis.scalepolicy;
import info.monitorenter.gui.chart.IAxis;
import info.monitorenter.gui.chart.IAxisLabelFormatter;
import info.monitorenter.gui.chart.IAxisScalePolicy;
import info.monitorenter.gui.chart.LabeledValue;
import info.monitorenter.gui.chart.axis.AAxis;
import info.monitorenter.gui.chart.labelformatters.LabelFormatterNumber;
import info.monitorenter.util.Range;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.List;
/**
* Scale policy implementation that ensures the following:
* <ul>
* <li>No label will overwrite the following label.</li>
* <li>No two labels will have the same value.</li>
* <li>Every tick will exactly show the value without rounding errors.</li>
* <li>Always the closest next possible tick is chosen regardless whether it is
* a major tick or a minor tick (subject to change in favor of major ticks)</li>
* </ul>
* <p>
*
* While this strategy is quite comfortable and prevents visual oddities there
* are some consequences to it:
*
* <ul>
* <li>Major ticks are not guaranteed to be shown. This is because a label of a
* minor tick label may need so much space that the following major tick has to
* be skipped (subject to change)</li>
* <li>Detailed control is not easy. E.g. if you want to enforce more ticks to
* show up you could:
* <ul>
* <li>Set an {@link LabelFormatterNumber} via
* {@link IAxis#setFormatter(IAxisLabelFormatter)} that formats little to no
* digits. But this could have both effects: More labels as the labels take less
* space or less labels as the value range is so little that an increased
* formatted value is possible only little times within that range.</li>
* <li>Choose major and minor ticks via
* {@link IAxis#setMinorTickSpacing(double)} and
* {@link IAxis#setMajorTickSpacing(double)}</li>
* </ul>
* </li>
* <li>Performance is not the best. This is because the space for a label has to
* be computed and pixels have to be transformed from and to the value domain.</li>
* </ul>
* <p>
*
*
*
* @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
*
*/
public class AxisScalePolicyManualTicks implements IAxisScalePolicy {
/**
* Just a helper to create a labeled value instance.
* <p>
*
* @param value
* the value use.
*
* @param majorTickSpacing
* if value % majorTickSpacing is zero then the resulting <code>
* {@link LabeledValue}</code> will be judged as a major tick.
*
* @param axis
* needed to re-parse the value into the label string by using the
* formatter of it.
*
* @return the value rounded to minor or major ticks.
*/
protected LabeledValue createLabeledValue(final double value, final double majorTickSpacing,
final IAxis<?> axis) {
final LabeledValue ret = new LabeledValue();
ret.setValue(value);
if (value % majorTickSpacing == 0) {
ret.setMajorTick(true);
} else {
ret.setMajorTick(false);
}
// format label string.
ret.setLabel(axis.getFormatter().format(ret.getValue()));
// as formatting rounds too, reparse value so that it is exactly at the
// point the label string describes.
ret.setValue(axis.getFormatter().parse(ret.getLabel()).doubleValue());
return ret;
}
/**
* Returns the labels for this axis.
* <p>
*
*
* @return the labels for the axis.
*/
protected List<LabeledValue> getLabels(final IAxis<?> axis) {
final List<LabeledValue> collect = new LinkedList<LabeledValue>();
double minorTickSpacing = axis.getMinorTickSpacing();
double majorTickSpacing = axis.getMajorTickSpacing();
if (minorTickSpacing > 0) {
final Range domain = axis.getRange();
final double min = domain.getMin();
final double max = domain.getMax();
String oldLabelName = "";
LabeledValue label;
final double range = max - min;
double value;
if (axis.isStartMajorTick()) {
value = ((int) (min / majorTickSpacing)) * (majorTickSpacing);
if (value < min) {
value += majorTickSpacing;
}
} else {
value = ((int) (min / minorTickSpacing)) * (minorTickSpacing);
}
String labelName = "start";
int loopStop = 0;
// first tick, manual init
while ((value <= max) && (loopStop < 100)) {
if (loopStop == 99) {
if (AAxis.DEBUG) {
System.out.println(axis.getAccessor().toString() + " axis: loop to high");
}
}
if (oldLabelName.equals(labelName)) {
if (AAxis.DEBUG) {
System.out.println("constant Label " + labelName);
}
}
label = this.createLabeledValue(value, majorTickSpacing, axis);
oldLabelName = labelName;
labelName = label.getLabel();
value = label.getValue();
loopStop++;
if ((value <= max) && (value >= min)) {
collect.add(label);
} else if (value > max) {
if (AAxis.DEBUG) {
System.out.println("Dropping label (too high) : (" + label + ")[max: " + max + "]");
}
} else if (value < min) {
if (AAxis.DEBUG) {
System.out.println("Dropping label (too low) : (" + label + ")[min: " + min + "]");
}
}
value += minorTickSpacing;
}
final int stop = collect.size();
for (int i = 0; i < stop; i++) {
label = collect.get(i);
label.setValue((label.getValue() - min) / range);
}
}
return collect;
}
/**
* @see info.monitorenter.gui.chart.IAxisScalePolicy#getScaleValues(java.awt.Graphics,
* info.monitorenter.gui.chart.IAxis)
*/
public List<LabeledValue> getScaleValues(final Graphics g2d, final IAxis<?> axis) {
return this.getLabels(axis);
}
/**
* @see info.monitorenter.gui.chart.IAxisScalePolicy#initPaintIteration(info.monitorenter.gui.chart.IAxis)
*/
public void initPaintIteration(IAxis<?> axis) {
// nop
}
}