JavaFX 2.x:Y轴上的对数标度 [英] JavaFX 2.x : Logarithmic scale on Y axis

查看:149
本文介绍了JavaFX 2.x:Y轴上的对数标度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从这篇很好的帖子开始

Java FX 2中的对数比例

我已经更改了此类,以获取Y轴上的对数刻度,并且工作正常.我唯一的问题是水平网格线很少,并且比例尺始终从0或接近零开始.

I have changed this class to get log scale on Y axis, and it works fine. The only problem I have is that there are very few horizontal grid lines and scale always start ranges from 0 or near zero.

这就是我得到的

我希望在我的数据系列的最小和最大范围内也具有刻度值网格,在这种情况下,min = 19,35 max = 20,35;截至目前,所有10条水平网格线均已绘制在该范围之外.

I would like to have tick values grid also in the min and max range of my data serie, in this case min = 19,35 max = 20,35; as of now all 10 horizontal grid lines are all plotted outside this range.

如何做到这一点?

谢谢,这是我的Y轴登录代码

Thanks all, here is my log code for Y axis

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.chart.ValueAxis;

//http://blog.dooapp.com/logarithmic-scale-strikes-back-in-javafx-20        
public class LogarithmicAxis extends ValueAxis<Number> {

//Create our LogarithmicAxis class that extends ValueAxis<Number> and define two properties that will represent the log lower and upper bounds of our axis.     
private final DoubleProperty logUpperBound = new SimpleDoubleProperty();
private final DoubleProperty logLowerBound = new SimpleDoubleProperty();
//

//we bind our properties with the default bounds of the value axis. But before, we should verify the given range according to the mathematic logarithmic interval definition.
public LogarithmicAxis() {
    super(1, 100);
    bindLogBoundsToDefaultBounds();
}

public LogarithmicAxis(double lowerBound, double upperBound) {
    super(lowerBound, upperBound);
try {
    validateBounds(lowerBound, upperBound);
    bindLogBoundsToDefaultBounds();
} catch (IllegalLogarithmicRangeException e) {
    }
}

/**
 * Bind our logarithmic bounds with the super class bounds, consider the base 10 logarithmic scale.
 */
private void bindLogBoundsToDefaultBounds() {
    logLowerBound.bind(new DoubleBinding() {
        {
            super.bind(lowerBoundProperty());
        }

        @Override
        protected double computeValue() {
            return Math.log10(lowerBoundProperty().get());
        }
    });
    logUpperBound.bind(new DoubleBinding() {
        {
            super.bind(upperBoundProperty());
        }

        @Override
        protected double computeValue() {
            return Math.log10(upperBoundProperty().get());
        }
    });
}

/**
 * Validate the bounds by throwing an exception if the values are not conform to the mathematics log interval:
 * ]0,Double.MAX_VALUE]
 *
 * @param lowerBound
 * @param upperBound
 * @throws IllegalLogarithmicRangeException
 */
private void validateBounds(double lowerBound, double upperBound) throws IllegalLogarithmicRangeException {
    if (lowerBound < 0 || upperBound < 0 || lowerBound > upperBound) {
        throw new IllegalLogarithmicRangeException(
                "The logarithmic range should be include to ]0,Double.MAX_VALUE] and the lowerBound should be less than the upperBound");
    }
}

//Now we have to implement all abstract methods of the ValueAxis class.
//The first one, calculateMinorTickMarks is used to get the list of minor tick marks position that you want to display on the axis. You could find my definition below. It's based on the number of minor tick and the logarithmic formula.
@Override
protected List<Number> calculateMinorTickMarks() {
    Number[] range = getRange();
    List<Number> minorTickMarksPositions = new ArrayList<>();
    if (range != null) {

        Number lowerBound = range[0];
        Number upperBound = range[1];
        double logUpperBound = Math.log10(upperBound.doubleValue());
        double logLowerBound = Math.log10(lowerBound.doubleValue());

        int minorTickMarkCount = getMinorTickCount();

        for (double i = logLowerBound; i <= logUpperBound; i += 1) {
            for (double j = 0; j <= 10; j += (1. / minorTickMarkCount)) {
                double value = j * Math.pow(10, i);
                minorTickMarksPositions.add(value);
            }
        }
    }
    return minorTickMarksPositions;
}

//Then, the calculateTickValues method is used to calculate a list of all the data values for each tick mark in range, represented by the second parameter. The formula is the same than previously but here we want to display one tick each power of 10.
@Override
protected List<Number> calculateTickValues(double length, Object range) {
    List<Number> tickPositions = new ArrayList<Number>();
    if (range != null) {
        Number lowerBound = ((Number[]) range)[0];
        Number upperBound = ((Number[]) range)[1];
        double logLowerBound = Math.log10(lowerBound.doubleValue());
        double logUpperBound = Math.log10(upperBound.doubleValue());
        System.out.println("lower bound is: " + lowerBound.doubleValue());

        for (double i = logLowerBound; i <= logUpperBound; i += 1) {
            for (double j = 1; j <= 10; j++) {
                double value = (j * Math.pow(10, i));
                tickPositions.add(value);
            }
        }
    }
    return tickPositions;
}

//The getRange provides the current range of the axis. A basic implementation is to return an array of the lowerBound and upperBound properties defined into the ValueAxis class.
@Override
protected Number[] getRange() {
    return new Number[] { lowerBoundProperty().get(), upperBoundProperty().get() };
}

//The getTickMarkLabel is only used to convert the number value to a string that will be displayed under the tickMark. Here I choose to use a number formatter.
@Override
protected String getTickMarkLabel(Number value) {
    NumberFormat formatter = NumberFormat.getInstance();
    formatter.setMaximumIntegerDigits(6);
    formatter.setMinimumIntegerDigits(1);
    return formatter.format(value);
}

//The method setRange is used to update the range when data are added into the chart. There is two possibilities, the axis is animated or not. The simplest case is to set the lower and upper bound properties directly with the new values.
@Override    
protected void setRange(Object range, boolean animate) {
    if (range != null) {
        Number lowerBound = ((Number[]) range)[0];
        Number upperBound = ((Number[]) range)[1];
        try {
            validateBounds(lowerBound.doubleValue(), upperBound.doubleValue());
        } catch (IllegalLogarithmicRangeException e) {
        }

        lowerBoundProperty().set(lowerBound.doubleValue());
        upperBoundProperty().set(upperBound.doubleValue());
    }
}

//We are almost done but we forgot to override 2 important methods that are used to perform the matching between data and the axis (and the reverse).
@Override
public Number getValueForDisplay(double displayPosition) {
    double delta = logUpperBound.get() - logLowerBound.get();
    if (getSide().isVertical()) {
        return Math.pow(10, (((displayPosition - getHeight()) / -getHeight()) * delta) + logLowerBound.get());
    } else {
        return Math.pow(10, (((displayPosition / getWidth()) * delta) + logLowerBound.get()));
    }
}

@Override
public double getDisplayPosition(Number value) {
    double delta = logUpperBound.get() - logLowerBound.get();
    double deltaV = Math.log10(value.doubleValue()) - logLowerBound.get();
    if (getSide().isVertical()) {
        return (1. - ((deltaV) / delta)) * getHeight();
    } else {
        return ((deltaV) / delta) * getWidth();
    }
}

/**
 * Exception to be thrown when a bound value isn't supported by the logarithmic axis<br>
 *
 *
 * @author Kevin Senechal mailto: kevin.senechal@dooapp.com
 *
 */
public class IllegalLogarithmicRangeException extends Exception {
/**
 * @param string
 */
    public IllegalLogarithmicRangeException(String message) {
        super(message);
    }
}
}

推荐答案

我认为您的问题是这样的:

I think your problem is this:

    super(1, 100);

文档:

Create a non-auto-ranging ValueAxis with the given upper & lower bound

尝试使用不带参数的构造函数,这将使边界自动调整范围.

Try using a constructor without parameters, which will make the boundaries auto-ranging.

您应该最终得到一个如下所示的构造函数:

You should end up with a constructor looking like this:

public LogarithmicAxis() {
    // was: super(1, 100);
    super();
    bindLogBoundsToDefaultBounds();
}

这篇关于JavaFX 2.x:Y轴上的对数标度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆