Skip to content

Commit

Permalink
Support decimal stepSize (chartjs#5786)
Browse files Browse the repository at this point in the history
  • Loading branch information
nagix authored and simonbrunel committed Nov 12, 2018
1 parent b62f52b commit 03cec64
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 23 deletions.
20 changes: 20 additions & 0 deletions src/core/core.helpers.js
Expand Up @@ -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;
Expand Down
47 changes: 24 additions & 23 deletions src/scales/scale.linearbase.js
Expand Up @@ -15,54 +15,55 @@ 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);
} else {
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;
}
Expand Down
11 changes: 11 additions & 0 deletions test/specs/core.helpers.tests.js
Expand Up @@ -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,
Expand Down
29 changes: 29 additions & 0 deletions test/specs/scale.linear.tests.js
Expand Up @@ -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({
Expand Down

0 comments on commit 03cec64

Please sign in to comment.