]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Support decimal stepSize (#5786)
authorAkihiko Kusanagi <nagi@nagi-p.com>
Mon, 12 Nov 2018 20:12:09 +0000 (04:12 +0800)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Mon, 12 Nov 2018 20:12:09 +0000 (21:12 +0100)
src/core/core.helpers.js
src/scales/scale.linearbase.js
test/specs/core.helpers.tests.js
test/specs/scale.linear.tests.js

index 5cd1d8f6e91c3a47fb1b0a2507e439bceff3c9f9..d8175af50c559fbdc6e51d7f5dda3c27885d54ac 100644 (file)
@@ -174,6 +174,26 @@ module.exports = function() {
        helpers.toDegrees = function(radians) {
                return radians * (180 / Math.PI);
        };
+
+       /**
+        * Returns the number of decimal places
+        * i.e. the number of digits after the decimal point, of the value of this Number.
+        * @param {Number} x - A number.
+        * @returns {Number} The number of decimal places.
+        */
+       helpers.decimalPlaces = function(x) {
+               if (!helpers.isFinite(x)) {
+                       return;
+               }
+               var e = 1;
+               var p = 0;
+               while (Math.round(x * e) / e !== x) {
+                       e *= 10;
+                       p++;
+               }
+               return p;
+       };
+
        // Gets the angle from vertical upright to the point about a centre.
        helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
                var distanceFromXCenter = anglePoint.x - centrePoint.x;
index 7408eb9f98927604a7b86b9cd1b7a00ab71d1e9e..0dc77fa36aaff0ea8da30f67406279d76bdeb146 100644 (file)
@@ -15,36 +15,41 @@ function generateTicks(generationOptions, dataRange) {
        // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
        // for details.
 
-       var factor;
-       var precision;
-       var spacing;
+       var stepSize = generationOptions.stepSize;
+       var min = generationOptions.min;
+       var max = generationOptions.max;
+       var spacing, precision, factor, niceRange, niceMin, niceMax, numSpaces;
 
-       if (generationOptions.stepSize && generationOptions.stepSize > 0) {
-               spacing = generationOptions.stepSize;
+       if (stepSize && stepSize > 0) {
+               spacing = stepSize;
        } else {
-               var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
+               niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
                spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
 
                precision = generationOptions.precision;
-               if (precision !== undefined) {
+               if (!helpers.isNullOrUndef(precision)) {
                        // If the user specified a precision, round to that number of decimal places
                        factor = Math.pow(10, precision);
                        spacing = Math.ceil(spacing * factor) / factor;
                }
        }
-       var niceMin = Math.floor(dataRange.min / spacing) * spacing;
-       var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
+       // If a precision is not specified, calculate factor based on spacing
+       if (!factor) {
+               factor = Math.pow(10, helpers.decimalPlaces(spacing));
+       }
+       niceMin = Math.floor(dataRange.min / spacing) * spacing;
+       niceMax = Math.ceil(dataRange.max / spacing) * spacing;
 
        // If min, max and stepSize is set and they make an evenly spaced scale use it.
-       if (!helpers.isNullOrUndef(generationOptions.min) && !helpers.isNullOrUndef(generationOptions.max) && generationOptions.stepSize) {
+       if (!helpers.isNullOrUndef(min) && !helpers.isNullOrUndef(max) && stepSize) {
                // If very close to our whole number, use it.
-               if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
-                       niceMin = generationOptions.min;
-                       niceMax = generationOptions.max;
+               if (helpers.almostWhole((max - min) / stepSize, spacing / 1000)) {
+                       niceMin = min;
+                       niceMax = max;
                }
        }
 
-       var numSpaces = (niceMax - niceMin) / spacing;
+       numSpaces = (niceMax - niceMin) / spacing;
        // If very close to our rounded value, use it.
        if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
                numSpaces = Math.round(numSpaces);
@@ -52,17 +57,13 @@ function generateTicks(generationOptions, dataRange) {
                numSpaces = Math.ceil(numSpaces);
        }
 
-       precision = 1;
-       if (spacing < 1) {
-               precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing)));
-               niceMin = Math.round(niceMin * precision) / precision;
-               niceMax = Math.round(niceMax * precision) / precision;
-       }
-       ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
+       niceMin = Math.round(niceMin * factor) / factor;
+       niceMax = Math.round(niceMax * factor) / factor;
+       ticks.push(helpers.isNullOrUndef(min) ? niceMin : min);
        for (var j = 1; j < numSpaces; ++j) {
-               ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
+               ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
        }
-       ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
+       ticks.push(helpers.isNullOrUndef(max) ? niceMax : max);
 
        return ticks;
 }
index 30baaf5acc1af53330af24dbaa74b01a02cd2b08..de6d0b41301351f43f3fcb92f1fc21ed2b85343e 100644 (file)
@@ -238,6 +238,17 @@ describe('Core helper tests', function() {
                expect(helpers.toDegrees(Math.PI * 3 / 2)).toBe(270);
        });
 
+       it('should get the correct number of decimal places', function() {
+               expect(helpers.decimalPlaces(100)).toBe(0);
+               expect(helpers.decimalPlaces(1)).toBe(0);
+               expect(helpers.decimalPlaces(0)).toBe(0);
+               expect(helpers.decimalPlaces(0.01)).toBe(2);
+               expect(helpers.decimalPlaces(-0.01)).toBe(2);
+               expect(helpers.decimalPlaces('1')).toBe(undefined);
+               expect(helpers.decimalPlaces('')).toBe(undefined);
+               expect(helpers.decimalPlaces(undefined)).toBe(undefined);
+       });
+
        it('should get an angle from a point', function() {
                var center = {
                        x: 0,
index 959a76db01ea796455af7328ed9e85a344e1376f..9ec21116e88fd4911ccdc0863a86443965f937f5 100644 (file)
@@ -573,6 +573,35 @@ describe('Linear Scale', function() {
                expect(chart.scales.yScale0.ticks).toEqual(['11', '9', '7', '5', '3', '1']);
        });
 
+       it('Should create decimal steps if stepSize is a decimal number', function() {
+               var chart = window.acquireChart({
+                       type: 'bar',
+                       data: {
+                               datasets: [{
+                                       yAxisID: 'yScale0',
+                                       data: [10, 3, 6, 8, 3, 1]
+                               }],
+                               labels: ['a', 'b', 'c', 'd', 'e', 'f']
+                       },
+                       options: {
+                               scales: {
+                                       yAxes: [{
+                                               id: 'yScale0',
+                                               type: 'linear',
+                                               ticks: {
+                                                       stepSize: 2.5
+                                               }
+                                       }]
+                               }
+                       }
+               });
+
+               expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
+               expect(chart.scales.yScale0.min).toBe(0);
+               expect(chart.scales.yScale0.max).toBe(10);
+               expect(chart.scales.yScale0.ticks).toEqual(['10', '7.5', '5', '2.5', '0']);
+       });
+
        describe('precision', function() {
                it('Should create integer steps if precision is 0', function() {
                        var chart = window.acquireChart({