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

Improve tick generation for linear scales #5938

Merged
merged 3 commits into from Jan 1, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/scales/scale.linear.js
Expand Up @@ -139,11 +139,11 @@ module.exports = function(Chart) {
var tickOpts = me.options.ticks;

if (me.isHorizontal()) {
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 40));
simonbrunel marked this conversation as resolved.
Show resolved Hide resolved
} else {
// The factor of 2 used to scale the font size has been experimentally determined.
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (1.5 * tickFontSize)));
simonbrunel marked this conversation as resolved.
Show resolved Hide resolved
}

return maxTicks;
Expand Down
26 changes: 22 additions & 4 deletions src/scales/scale.linearbase.js
Expand Up @@ -18,13 +18,30 @@ function generateTicks(generationOptions, dataRange) {
var stepSize = generationOptions.stepSize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stepSize < 0 doesn't seems a valid value, so maybe we can simplify all these checks by clamping this value:

var stepSize = Math.max(0, generationOptions.stepSize);
var unit = stepSize || 1;
//...

var min = generationOptions.min;
var max = generationOptions.max;
var spacing, precision, factor, niceRange, niceMin, niceMax, numSpaces;
var spacing, precision, factor, niceMin, niceMax, numSpaces, maxNumSpaces;

if (stepSize && stepSize > 0) {
spacing = stepSize;
if (generationOptions.maxTicksLimit) {
maxNumSpaces = generationOptions.maxTicksLimit - 1;
// spacing is set to stepSize multiplied by a nice number of
// Math.ceil((max - min) / maxNumSpaces / stepSize) = num of steps that should be grouped
spacing *= helpers.niceNum(Math.ceil((dataRange.max - dataRange.min) / maxNumSpaces / stepSize));
numSpaces = Math.ceil(dataRange.max / spacing) - Math.floor(dataRange.min / spacing);
if (numSpaces > maxNumSpaces) {
// If the calculated num of spaces exceeds maxNumSpaces, recalculate it
spacing = helpers.niceNum(Math.ceil(numSpaces * spacing / maxNumSpaces / stepSize)) * stepSize;
}
}
} else {
niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
maxNumSpaces = generationOptions.maxTicks - 1;
// spacing is set to a nice number of (max - min) / maxNumSpaces
spacing = helpers.niceNum((dataRange.max - dataRange.min) / maxNumSpaces);
numSpaces = Math.ceil(dataRange.max / spacing) - Math.floor(dataRange.min / spacing);
if (numSpaces > maxNumSpaces) {
// If the calculated num of spaces exceeds maxNumSpaces, recalculate it
spacing = helpers.niceNum(numSpaces * spacing / maxNumSpaces);
}
simonbrunel marked this conversation as resolved.
Show resolved Hide resolved

precision = generationOptions.precision;
if (!helpers.isNullOrUndef(precision)) {
Expand Down Expand Up @@ -155,14 +172,15 @@ module.exports = function(Chart) {
var tickOpts = opts.ticks;

// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// the axis area. For now, we say that the minimum tick spacing in pixels must be 40
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph. Make sure we always have at least 2 ticks
var maxTicks = me.getTickLimit();
maxTicks = Math.max(2, maxTicks);

var numericGeneratorOptions = {
maxTicks: maxTicks,
maxTicksLimit: tickOpts.maxTicksLimit,
simonbrunel marked this conversation as resolved.
Show resolved Hide resolved
min: tickOpts.min,
max: tickOpts.max,
precision: tickOpts.precision,
Expand Down
4 changes: 0 additions & 4 deletions src/scales/scale.logarithmic.js
Expand Up @@ -15,10 +15,6 @@ function generateTicks(generationOptions, dataRange) {
var ticks = [];
var valueOrDefault = helpers.valueOrDefault;

// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));

var endExp = Math.floor(helpers.log10(dataRange.max));
Expand Down
41 changes: 41 additions & 0 deletions test/specs/scale.linear.tests.js
Expand Up @@ -749,6 +749,47 @@ describe('Linear Scale', function() {
expect(chart.scales.yScale0.ticks).toEqual(['0.06', '0.05', '0.04', '0.03', '0.02', '0.01', '0']);
});

it('Should correctly limit the maximum number of ticks', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
labels: ['a', 'b'],
datasets: [{
data: [0.5, 2.5]
}]
},
options: {
scales: {
yAxes: [{
id: 'yScale'
}]
}
}
});

expect(chart.scales.yScale.ticks).toEqual(['2.5', '2.0', '1.5', '1.0', '0.5']);

chart.options.scales.yAxes[0].ticks.maxTicksLimit = 11;
chart.update();

expect(chart.scales.yScale.ticks).toEqual(['2.5', '2.0', '1.5', '1.0', '0.5']);

chart.options.scales.yAxes[0].ticks.maxTicksLimit = 21;
chart.update();

expect(chart.scales.yScale.ticks).toEqual([
'2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6',
'1.5', '1.4', '1.3', '1.2', '1.1', '1.0', '0.9', '0.8', '0.7', '0.6',
'0.5'
]);

chart.options.scales.yAxes[0].ticks.maxTicksLimit = 11;
chart.options.scales.yAxes[0].ticks.stepSize = 0.01;
chart.update();

expect(chart.scales.yScale.ticks).toEqual(['2.5', '2.0', '1.5', '1.0', '0.5']);
});

it('Should build labels using the user supplied callback', function() {
var chart = window.acquireChart({
type: 'bar',
Expand Down
31 changes: 31 additions & 0 deletions test/specs/scale.radialLinear.tests.js
Expand Up @@ -282,6 +282,37 @@ describe('Test the radial linear scale', function() {
expect(chart.scale.end).toBe(0);
});

it('Should correctly limit the maximum number of ticks', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [0.5, 1.5, 2.5]
}]
},
options: {
scale: {
pointLabels: {
display: false
}
}
}
});

expect(chart.scale.ticks).toEqual(['0.5', '1.0', '1.5', '2.0', '2.5']);

chart.options.scale.ticks.maxTicksLimit = 11;
chart.update();

expect(chart.scale.ticks).toEqual(['0.5', '1.0', '1.5', '2.0', '2.5']);

chart.options.scale.ticks.stepSize = 0.01;
chart.update();

expect(chart.scale.ticks).toEqual(['0.5', '1.0', '1.5', '2.0', '2.5']);
});

it('Should build labels using the user supplied callback', function() {
var chart = window.acquireChart({
type: 'radar',
Expand Down