From 71b03d89541b0d2c0b542db653e61c53f3834254 Mon Sep 17 00:00:00 2001 From: David Gilbert Date: Sat, 24 Oct 2020 14:11:57 +0200 Subject: [PATCH] Fix for bug #64, exception on extreme zoom on NumberAxis --- README.md | 1 + .../java/org/jfree/chart/axis/NumberAxis.java | 48 +++++++++++-------- .../org/jfree/chart/axis/NumberTickUnit.java | 3 +- .../chart/axis/NumberTickUnitSource.java | 2 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a68c9e2fa..cf15ee81b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ History ##### Version 1.5.1 (not yet released) - modify buffer in ChartPanel to handle high DPI displays (bug #170); +- fix for exception on extreme zoom on NumberAxis (bug #64); - added Catalan translations (PR #117); - fix for LayeredBarRenderer (bug #169); - migrated to JUnit 5. diff --git a/src/main/java/org/jfree/chart/axis/NumberAxis.java b/src/main/java/org/jfree/chart/axis/NumberAxis.java index 92fa1bca9..be5a6ab1a 100644 --- a/src/main/java/org/jfree/chart/axis/NumberAxis.java +++ b/src/main/java/org/jfree/chart/axis/NumberAxis.java @@ -844,27 +844,36 @@ protected void selectHorizontalAutoTickUnit(Graphics2D g2, TickUnit unit = getTickUnit(); TickUnitSource tickUnitSource = getStandardTickUnits(); - // we should use the current tick unit if it gives a count in the range - // 2 to 40 otherwise just estimate one that will give a count <= 20 + + // we should start with the current tick unit if it gives a count in + // the range 3 to 40 otherwise estimate one that will give a count <= 10 double length = getRange().getLength(); int count = (int) (length / unit.getSize()); - if (count < 2 || count > 40) { - unit = tickUnitSource.getCeilingTickUnit(length / 20); + if (count < 3 || count > 40) { + unit = tickUnitSource.getCeilingTickUnit(length / 10); } - double tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit); + // now consider the label size relative to the width of the tick unit + // and make a guess at the ideal size TickUnit unit1 = tickUnitSource.getCeilingTickUnit(unit); - double unit1Width = lengthToJava2D(unit1.getSize(), dataArea, edge); - - // then extrapolate... + double tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit1); + double unit1Width = lengthToJava2D(unit1.getSize(), dataArea, edge); + NumberTickUnit unit2 = (NumberTickUnit) unit1; double guess = (tickLabelWidth / unit1Width) * unit1.getSize(); - NumberTickUnit unit2 = (NumberTickUnit) - tickUnitSource.getCeilingTickUnit(guess); - double unit2Width = lengthToJava2D(unit2.getSize(), dataArea, edge); - - tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit2); - if (tickLabelWidth > unit2Width) { - unit2 = (NumberTickUnit) tickUnitSource.getLargerTickUnit(unit2); + + // due to limitations of double precision, when you zoom very far into + // a chart, eventually the visible axis range will get reported as + // having length 0, and then 'guess' above will be infinite ... in that + // case we'll just stick with the tick unit we have, it's better than + // throwing an exception + // https://github.com/jfree/jfreechart/issues/64 + if (Double.isFinite(guess)) { + unit2 = (NumberTickUnit) tickUnitSource.getCeilingTickUnit(guess); + double unit2Width = lengthToJava2D(unit2.getSize(), dataArea, edge); + tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit2); + if (tickLabelWidth > unit2Width) { + unit2 = (NumberTickUnit) tickUnitSource.getLargerTickUnit(unit2); + } } setTickUnit(unit2, false, false); } @@ -901,7 +910,6 @@ protected void selectVerticalAutoTickUnit(Graphics2D g2, if (tickLabelHeight > unit2Height) { unit2 = (NumberTickUnit) tickUnits.getLargerTickUnit(unit2); } - setTickUnit(unit2, false, false); } @@ -1006,8 +1014,8 @@ protected List refreshTicksHorizontal(Graphics2D g2, } } - Tick tick = new NumberTick(new Double(currentTickValue), - tickLabel, anchor, rotationAnchor, angle); + Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, + rotationAnchor, angle); result.add(tick); double nextTickValue = lowestTickValue + ((i + 1) * size); for (int minorTick = 1; minorTick < minorTickSpaces; @@ -1106,8 +1114,8 @@ protected List refreshTicksVertical(Graphics2D g2, } } - Tick tick = new NumberTick(new Double(currentTickValue), - tickLabel, anchor, rotationAnchor, angle); + Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, + rotationAnchor, angle); result.add(tick); double nextTickValue = lowestTickValue + ((i + 1) * size); diff --git a/src/main/java/org/jfree/chart/axis/NumberTickUnit.java b/src/main/java/org/jfree/chart/axis/NumberTickUnit.java index 25edda9d3..176f06555 100644 --- a/src/main/java/org/jfree/chart/axis/NumberTickUnit.java +++ b/src/main/java/org/jfree/chart/axis/NumberTickUnit.java @@ -147,7 +147,8 @@ public boolean equals(Object obj) { */ @Override public String toString() { - return "[size=" + this.valueToString(this.getSize()) + "]"; + return "[NumberTickUnit: size=" + this.valueToString(this.getSize()) + + ", formatter=" + this.formatter + "]"; } /** diff --git a/src/main/java/org/jfree/chart/axis/NumberTickUnitSource.java b/src/main/java/org/jfree/chart/axis/NumberTickUnitSource.java index 6339b8b9a..7d0c01335 100644 --- a/src/main/java/org/jfree/chart/axis/NumberTickUnitSource.java +++ b/src/main/java/org/jfree/chart/axis/NumberTickUnitSource.java @@ -27,7 +27,7 @@ * ------------------------- * NumberTickUnitSource.java * ------------------------- - * (C) Copyright 2014, by Object Refinery Limited. + * (C) Copyright 2014-2020, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -;