]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix generateTicks when working with tiny numbers (#5948)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Wed, 9 Jan 2019 06:58:13 +0000 (08:58 +0200)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Wed, 9 Jan 2019 06:58:13 +0000 (07:58 +0100)
src/scales/scale.linearbase.js
test/specs/scale.linear.tests.js

index 808f454407a6eeabac8dd6e9e152a07e8bd8faea..057e68ffc3d4f18c4d875930fa3e59719155e49f 100644 (file)
@@ -4,6 +4,7 @@ var helpers = require('../helpers/index');
 var Scale = require('../core/core.scale');
 
 var noop = helpers.noop;
+var isNullOrUndef = helpers.isNullOrUndef;
 
 /**
  * Generate a set of linear ticks
@@ -17,24 +18,31 @@ function generateTicks(generationOptions, dataRange) {
        // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
        // for details.
 
+       var MIN_SPACING = 1e-14;
        var stepSize = generationOptions.stepSize;
        var unit = stepSize || 1;
        var maxNumSpaces = generationOptions.maxTicks - 1;
        var min = generationOptions.min;
        var max = generationOptions.max;
        var precision = generationOptions.precision;
-       var spacing, factor, niceMin, niceMax, numSpaces;
+       var rmin = dataRange.min;
+       var rmax = dataRange.max;
+       var spacing = helpers.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit;
+       var factor, niceMin, niceMax, numSpaces;
+
+       // Beyond MIN_SPACING floating point numbers being to lose precision
+       // such that we can't do the math necessary to generate ticks
+       if (spacing < MIN_SPACING && isNullOrUndef(min) && isNullOrUndef(max)) {
+               return [rmin, rmax];
+       }
 
-       // spacing is set to a nice number of the dataRange divided by maxNumSpaces.
-       // stepSize is used as a minimum unit if it is specified.
-       spacing = helpers.niceNum((dataRange.max - dataRange.min) / maxNumSpaces / unit) * unit;
-       numSpaces = Math.ceil(dataRange.max / spacing) - Math.floor(dataRange.min / spacing);
+       numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
        if (numSpaces > maxNumSpaces) {
                // If the calculated num of spaces exceeds maxNumSpaces, recalculate it
                spacing = helpers.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit;
        }
 
-       if (stepSize || helpers.isNullOrUndef(precision)) {
+       if (stepSize || isNullOrUndef(precision)) {
                // If a precision is not specified, calculate factor based on spacing
                factor = Math.pow(10, helpers.decimalPlaces(spacing));
        } else {
@@ -43,16 +51,16 @@ function generateTicks(generationOptions, dataRange) {
                spacing = Math.ceil(spacing * factor) / factor;
        }
 
-       niceMin = Math.floor(dataRange.min / spacing) * spacing;
-       niceMax = Math.ceil(dataRange.max / spacing) * spacing;
+       niceMin = Math.floor(rmin / spacing) * spacing;
+       niceMax = Math.ceil(rmax / spacing) * spacing;
 
        // If min, max and stepSize is set and they make an evenly spaced scale use it.
        if (stepSize) {
                // If very close to our whole number, use it.
-               if (!helpers.isNullOrUndef(min) && helpers.almostWhole(min / spacing, spacing / 1000)) {
+               if (!isNullOrUndef(min) && helpers.almostWhole(min / spacing, spacing / 1000)) {
                        niceMin = min;
                }
-               if (!helpers.isNullOrUndef(max) && helpers.almostWhole(max / spacing, spacing / 1000)) {
+               if (!isNullOrUndef(max) && helpers.almostWhole(max / spacing, spacing / 1000)) {
                        niceMax = max;
                }
        }
@@ -67,11 +75,11 @@ function generateTicks(generationOptions, dataRange) {
 
        niceMin = Math.round(niceMin * factor) / factor;
        niceMax = Math.round(niceMax * factor) / factor;
-       ticks.push(helpers.isNullOrUndef(min) ? niceMin : min);
+       ticks.push(isNullOrUndef(min) ? niceMin : min);
        for (var j = 1; j < numSpaces; ++j) {
                ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
        }
-       ticks.push(helpers.isNullOrUndef(max) ? niceMax : max);
+       ticks.push(isNullOrUndef(max) ? niceMax : max);
 
        return ticks;
 }
index a186c6cc7fedcb8d08b45536f67a5716d6c49453..bdf8a9386f9bed84025547ecfe8bcc7e303ef89f 100644 (file)
@@ -1170,4 +1170,29 @@ describe('Linear Scale', function() {
                expect(data[0]._model.base + minBarLength).toEqual(data[0]._model.x);
                expect(data[1]._model.base - minBarLength).toEqual(data[1]._model.x);
        });
+
+       it('Should generate max and min that are not equal when data contains values that are very close to each other', function() {
+               var chart = window.acquireChart({
+                       type: 'scatter',
+                       data: {
+                               datasets: [{
+                                       data: [
+                                               {x: 1, y: 1.8548483304974972},
+                                               {x: 2, y: 1.8548483304974974},
+                                       ]
+                               }],
+                       },
+                       options: {
+                               scales: {
+                                       yAxes: [{
+                                               id: 'yScale0',
+                                               type: 'linear',
+                                       }]
+                               }
+                       }
+               });
+
+               expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
+               expect(chart.scales.yScale0.max).toBeGreaterThan(chart.scales.yScale0.min);
+       });
 });