Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support decimal stepSize #5786

Merged
merged 1 commit into from Nov 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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) {
simonbrunel marked this conversation as resolved.
Show resolved Hide resolved
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