From 7a65546629d8561d63eccb3b07ae44bce54d7595 Mon Sep 17 00:00:00 2001 From: Carl Osterwisch Date: Sun, 16 Sep 2018 05:33:48 -0400 Subject: [PATCH] Fix scale when data is all small numbers (#5723) * Add test for correct handling of small numbers * Calculate tick precision for arbitrarily small numbers * Use scientific notation for very small tick numbers * Calculate significant digits for exponential tick values --- src/core/core.ticks.js | 12 +++++++++--- src/scales/scale.linearbase.js | 2 +- test/specs/scale.linear.tests.js | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/core/core.ticks.js b/src/core/core.ticks.js index 3898842a49a..f63e525983a 100644 --- a/src/core/core.ticks.js +++ b/src/core/core.ticks.js @@ -46,9 +46,15 @@ module.exports = { var tickString = ''; if (tickValue !== 0) { - var numDecimal = -1 * Math.floor(logDelta); - numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places - tickString = tickValue.toFixed(numDecimal); + var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); + if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation + var logTick = helpers.log10(Math.abs(tickValue)); + tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta)); + } else { + var numDecimal = -1 * Math.floor(logDelta); + numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } } else { tickString = '0'; // never show decimal places for 0 } diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index c78dc64f298..7408eb9f989 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -54,7 +54,7 @@ function generateTicks(generationOptions, dataRange) { precision = 1; if (spacing < 1) { - precision = Math.pow(10, spacing.toString().length - 2); + precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing))); niceMin = Math.round(niceMin * precision) / precision; niceMax = Math.round(niceMax * precision) / precision; } diff --git a/test/specs/scale.linear.tests.js b/test/specs/scale.linear.tests.js index ab046078391..380feaac09d 100644 --- a/test/specs/scale.linear.tests.js +++ b/test/specs/scale.linear.tests.js @@ -212,6 +212,31 @@ describe('Linear Scale', function() { expect(chart.scales.yScale0.max).toBe(90); }); + it('Should correctly determine the max & min data values for small numbers', function() { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + yAxisID: 'yScale0', + data: [-1e-8, 3e-8, -4e-8, 6e-8] + }], + labels: ['a', 'b', 'c', 'd'] + }, + options: { + scales: { + yAxes: [{ + id: 'yScale0', + type: 'linear' + }] + } + } + }); + + expect(chart.scales.yScale0).not.toEqual(undefined); // must construct + expect(chart.scales.yScale0.min * 1e8).toBeCloseTo(-4); + expect(chart.scales.yScale0.max * 1e8).toBeCloseTo(6); + }); + it('Should correctly determine the max & min for scatter data', function() { var chart = window.acquireChart({ type: 'line',