package org.radargun.reporting.html; import java.awt.*; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberTick; import org.jfree.chart.axis.NumberTickUnitSource; import org.jfree.chart.axis.TickType; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.data.Range; import org.jfree.ui.RectangleEdge; import org.jfree.ui.TextAnchor; /** * Inverse logarithmic axis 0 .. limit < 100 * * @author Radim Vansa <rvansa@redhat.com> */ class PercentileLogAxis extends ValueAxis { protected static final double TICK_LABEL_ANGLE = Math.PI / 4; private final double limit; private final double totalTicks; PercentileLogAxis(String label, double limit) { super(label, new NumberTickUnitSource()); this.limit = limit; totalTicks = computeTicks(limit); } @Override public void configure() { } @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { AxisState state; // if the axis is not visible, don't draw it... if (!isVisible()) { state = new AxisState(cursor); // even though the axis is not visible, we need ticks for the // gridlines... java.util.List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); return state; } 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; } @Override public java.util.List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { if (RectangleEdge.isTopOrBottom(edge)) { return refreshTicksHorizontal(g2, dataArea, edge); } else throw new UnsupportedOperationException(); } protected java.util.List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { Range range = getRange(); java.util.List ticks = new ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); TextAnchor textAnchor; if (edge == RectangleEdge.TOP) { textAnchor = TextAnchor.BOTTOM_RIGHT; } else { textAnchor = TextAnchor.TOP_LEFT; } DecimalFormat formatter = new DecimalFormat("##.#%"); int tick = 1; ticks.add(new NumberTick(TickType.MINOR, 0.5, "50%", textAnchor, TextAnchor.TOP_LEFT, TICK_LABEL_ANGLE)); for (; ; ) { formatter.setMinimumFractionDigits(tick - 2); formatter.setMaximumFractionDigits(tick - 1); double current = computeInverseTicks(tick); if (current > limit) break; if (range.contains(current)) { ticks.add(new NumberTick(TickType.MAJOR, current, formatter.format(current), textAnchor, textAnchor, TICK_LABEL_ANGLE)); } current = current + (1 - current) / 2; if (current > limit) break; ticks.add(new NumberTick(TickType.MINOR, current, formatter.format(current), textAnchor, TextAnchor.TOP_LEFT, TICK_LABEL_ANGLE)); ++tick; } return ticks; } protected double findMaximumTickLabelHeight(List ticks, Graphics2D g2, Rectangle2D drawArea, boolean vertical) { return ChartsHelper.getMaximumTickLabelHeight(ticks, g2, getTickLabelInsets(), getTickLabelFont(), TICK_LABEL_ANGLE); } protected double findMaximumTickLabelWidth(List ticks, Graphics2D g2, Rectangle2D drawArea, boolean vertical) { return ChartsHelper.getMaximumTickLabelWidth(ticks, g2, getTickLabelInsets(), getTickLabelFont(), TICK_LABEL_ANGLE); } @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { double valueTicks = computeTicks(value); double min = 0.0, max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { max = area.getMinY(); min = area.getMaxY(); } return min + (valueTicks / totalTicks) * (max - min); } @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { double min = 0.0, max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getY(); } double valueTicks = totalTicks * (java2DValue - min) / (max - min); return computeInverseTicks(valueTicks); } private double computeTicks(double value) { return -Math.log10(1 - value); } private double computeInverseTicks(double valueTicks) { return 1 - Math.pow(10d, -valueTicks); } @Override protected void autoAdjustRange() { // nothing to do, the range is fixed to 0 .. limit } }