JavaFX NumberAxis AutoRange无限循环

时间:2015-09-10 23:58:23

标签: java javafx java-8 linechart

我有一个LineChart,其中Y轴设置为自动范围。由于NumberAxis.autoRange()卡在无限循环中,JavaFx线程偶尔会挂起。工作线程生成的新数据,然后每隔几秒就添加到图表(在JFX线程上)。无限循环发生在此代码中(取自NumberAxis.autoRange()):

for (double major = minRounded; major <= maxRounded; major += tickUnitRounded, count ++)  {
   double size = side.isVertical() ? measureTickMarkSize(major, getTickLabelRotation(), formatter).getHeight() :
                                        measureTickMarkSize(major, getTickLabelRotation(), formatter).getWidth();
   if (major == minRounded) { // first
      last = size/2;
   } else {
      maxReqTickGap = Math.max(maxReqTickGap, last + 6 + (size/2) );
   }
}

从调试开始,每次都看到if (major == minRoundeed)条件为true。因此,major变量不得更新。

我没有带有本地变量调试信息的NumberAxis类的编译版本,所以我看不到局部变量是什么。构建JavaFX Runtime类似乎需要做很多工作,但可能是下一步。

我无法可靠地重现此问题,因此无法提供Minimal, Complete, and Verifiable example。我没有在Oracle或OpenJDK错误数据库中记录任何问题。

JDK版本:8u60

编辑:

我向Oracle报告了这个错误,目前正在等待他们接受它。

1 个答案:

答案 0 :(得分:1)

问题

意味着循环将依赖于双值。因此,如果您尝试为minValue和maxValue采用如此小的double值,它将失败。

错误与否?

对我而言,这不像是一个错误。你可以问自己,如果你真的想在轴上显示如此大的分数,或者你能更好地扩展它们吗?您的应用程序的用户可能更乐意阅读基于轴标签的基础,而不是0.00000000000000000000000000000000000000015或1.5E-33?

在整个Java API中还有更多的东西也会发生这种情况,因为它是一个简单的数字溢出。

一个简单的例子

这将证明,如果值太小,它将循环无限。

import javafx.geometry.Side;


public class AutoRangeTester {

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    AutoRangeTester art = new AutoRangeTester();
    art.autoRange(Double.MIN_VALUE, Double.MIN_VALUE + 0.000000000000000000000000000000001, 100, 50);
  }

  /**
   * Called to set the upper and lower bound and anything else that needs to be
   * auto-ranged
   *
   * @param minValue  The min data value that needs to be plotted on this axis
   * @param maxValue  The max data value that needs to be plotted on this axis
   * @param length    The length of the axis in display coordinates
   * @param labelSize The approximate average size a label takes along the axis
   *
   * @return The calculated range
   */
  public Object autoRange(double minValue, double maxValue, double length,
                          double labelSize) {
    final Side side = Side.LEFT;
    // check if we need to force zero into range
    if (true) {
      if (maxValue < 0) {
        maxValue = 0;
      } else if (minValue > 0) {
        minValue = 0;
      }
    }
    final double range = maxValue - minValue;
    // pad min and max by 2%, checking if the range is zero
    final double paddedRange = (range == 0) ? 2 : Math.abs(range) * 1.02;
    final double padding = (paddedRange - range) / 2;
    // if min and max are not zero then add padding to them
    double paddedMin = minValue - padding;
    double paddedMax = maxValue + padding;
    // check padding has not pushed min or max over zero line
    if ((paddedMin < 0 && minValue >= 0) || (paddedMin > 0 && minValue <= 0)) {
      // padding pushed min above or below zero so clamp to 0
      paddedMin = 0;
    }
    if ((paddedMax < 0 && maxValue >= 0) || (paddedMax > 0 && maxValue <= 0)) {
      // padding pushed min above or below zero so clamp to 0
      paddedMax = 0;
    }
    // calculate the number of tick-marks we can fit in the given length
    int numOfTickMarks = (int) Math.floor(length / labelSize);
    // can never have less than 2 tick marks one for each end
    numOfTickMarks = Math.max(numOfTickMarks, 2);
    // calculate tick unit for the number of ticks can have in the given data range
    double tickUnit = paddedRange / (double) numOfTickMarks;
    // search for the best tick unit that fits
    double tickUnitRounded = 0;
    double minRounded = 0;
    double maxRounded = 0;
    int count = 0;
    double reqLength = Double.MAX_VALUE;

    // loop till we find a set of ticks that fit length and result in a total of less than 20 tick marks
    while (reqLength > length || count > 20) {
      int exp = (int) Math.floor(Math.log10(tickUnit));
      final double mant = tickUnit / Math.pow(10, exp);
      double ratio = mant;
      if (mant > 5d) {
        exp++;
        ratio = 1;
      } else if (mant > 1d) {
        ratio = mant > 2.5 ? 5 : 2.5;
      }

      tickUnitRounded = ratio * Math.pow(10, exp);

      minRounded = Math.floor(paddedMin / tickUnitRounded) * tickUnitRounded;
      maxRounded = Math.ceil(paddedMax / tickUnitRounded) * tickUnitRounded;

      count = 0;
      for (double major = minRounded; major <= maxRounded; major
              += tickUnitRounded, count++) {
        System.out.println("minRounded: " + minRounded);
        System.out.println("maxRounded: " + maxRounded);
        System.out.println("major: " + major);
        System.out.println("tickUnitRounded: " + tickUnitRounded);
        System.out.println("-------------------------------------");
      }

    }
    return null;
  }

}

更新

错误报告:https://bugs.openjdk.java.net/browse/JDK-8136535 计划针对版本9进行修复。