Better default tick rotation and tick autoskip settings (#2258)

* Better default tick rotation and tick autoskip settings

* scale.time: Use ctx to measure label, and <= instead of < for unit fitting

* Test Changes

* Passing Tests with new defaults
This commit is contained in:
Tanner Linsley
2016-04-16 17:38:03 -05:00
parent 1be88df813
commit 4f60eecc01
11 changed files with 2358 additions and 2375 deletions

View File

@@ -31,13 +31,13 @@ module.exports = function(Chart) {
// label settings
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
autoSkip: true,
autoSkipPadding: 20,
autoSkipPadding: 0,
callback: function(value) {
return '' + value;
}
@@ -682,4 +682,4 @@ module.exports = function(Chart) {
}
}
});
};
};

View File

@@ -62,7 +62,7 @@ module.exports = function(Chart) {
}
},
ticks: {
autoSkip: false,
autoSkip: false
}
};
@@ -137,6 +137,13 @@ module.exports = function(Chart) {
},
buildTicks: function(index) {
this.ctx.save();
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
this.ctx.font = tickLabelFont;
this.ticks = [];
this.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
this.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc)
@@ -149,16 +156,15 @@ module.exports = function(Chart) {
this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, 1);
} else {
// Determine the smallest needed unit of the time
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var innerWidth = this.isHorizontal() ? this.width - (this.paddingLeft + this.paddingRight) : this.height - (this.paddingTop + this.paddingBottom);
// Crude approximation of what the label length might be
var tempFirstLabel = this.tickFormatFunction(this.firstTick, 0, []);
var tickLabelWidth = tempFirstLabel.length * tickFontSize;
var tickLabelWidth = this.ctx.measureText(tempFirstLabel).width;
var cosRotation = Math.cos(helpers.toRadians(this.options.ticks.maxRotation));
var sinRotation = Math.sin(helpers.toRadians(this.options.ticks.maxRotation));
tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
var labelCapacity = innerWidth / (tickLabelWidth + 10);
var labelCapacity = innerWidth / (tickLabelWidth);
// Start as small as possible
this.tickUnit = 'millisecond';
@@ -176,7 +182,7 @@ module.exports = function(Chart) {
if (helpers.isArray(unitDefinition.steps) && Math.ceil(this.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) {
// Use one of the prefedined steps
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
if (unitDefinition.steps[idx] > Math.ceil(this.scaleSizeInUnits / labelCapacity)) {
if (unitDefinition.steps[idx] >= Math.ceil(this.scaleSizeInUnits / labelCapacity)) {
this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, unitDefinition.steps[idx]);
break;
}
@@ -257,6 +263,7 @@ module.exports = function(Chart) {
this.lastTick = this.ticks[this.ticks.length - 1].clone();
}
}
this.ctx.restore();
},
// Get tooltip label
getLabelForIndex: function(index, datasetIndex) {
@@ -335,4 +342,4 @@ module.exports = function(Chart) {
});
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
};
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -230,14 +230,14 @@ describe('Core helper tests', function() {
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[1].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
},
type: 'linear'
}, {
@@ -260,14 +260,14 @@ describe('Core helper tests', function() {
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[2].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
},
type: 'linear'
}]

View File

@@ -49,21 +49,21 @@ describe('Test the layout service', function() {
left: 50,
right: 250,
top: 0,
bottom: 81.0423977855504,
bottom: 83.6977778440511,
});
// Is xScale at the right spot
expect(xScale.left).toBe(50);
expect(xScale.right).toBe(250);
expect(xScale.top).toBe(81.0423977855504);
expect(xScale.top).toBe(83.6977778440511);
expect(xScale.bottom).toBe(150);
expect(xScale.labelRotation).toBe(55);
expect(xScale.labelRotation).toBe(50);
// Is yScale at the right spot
expect(yScale.left).toBe(0);
expect(yScale.right).toBe(50);
expect(yScale.top).toBe(0);
expect(yScale.bottom).toBe(81.0423977855504);
expect(yScale.bottom).toBe(83.6977778440511);
});
it('should fit scales that are in the top and right positions', function() {
@@ -116,7 +116,7 @@ describe('Test the layout service', function() {
expect(chartInstance.chartArea).toEqual({
left: 0,
right: 200,
top: 68.9576022144496,
top: 66.3022221559489,
bottom: 150,
});
@@ -124,13 +124,13 @@ describe('Test the layout service', function() {
expect(xScale.left).toBe(0);
expect(xScale.right).toBe(200);
expect(xScale.top).toBe(0);
expect(xScale.bottom).toBe(68.9576022144496);
expect(xScale.labelRotation).toBe(55);
expect(xScale.bottom).toBe(66.3022221559489);
expect(xScale.labelRotation).toBe(50);
// Is yScale at the right spot
expect(yScale.left).toBe(200);
expect(yScale.right).toBe(250);
expect(yScale.top).toBe(68.9576022144496);
expect(yScale.top).toBe(66.3022221559489);
expect(yScale.bottom).toBe(150);
});
@@ -196,30 +196,30 @@ describe('Test the layout service', function() {
left: 110,
right: 250,
top: 0,
bottom: 75,
bottom: 83.6977778440511,
});
// Is xScale at the right spot
expect(xScale.left).toBe(110);
expect(xScale.right).toBe(250);
expect(xScale.top).toBe(75);
expect(xScale.top).toBe(83.6977778440511);
expect(xScale.bottom).toBe(150);
// Are yScales at the right spot
expect(yScale1.left).toBe(0);
expect(yScale1.right).toBe(50);
expect(yScale1.top).toBe(0);
expect(yScale1.bottom).toBe(75);
expect(yScale1.bottom).toBe(83.6977778440511);
expect(yScale2.left).toBe(50);
expect(yScale2.right).toBe(110);
expect(yScale2.top).toBe(0);
expect(yScale2.bottom).toBe(75);
expect(yScale2.bottom).toBe(83.6977778440511);
});
// This is an oddball case. What happens is, when the scales are fit the first time they must fit within the assigned size. In this case,
// the labels on the xScale need to rotate to fit. However, when the scales are fit again after the width of the left axis is determined,
// the labels do not need to rotate. Previously, the chart was too small because the chartArea did not expand to take up the space freed up
// the labels on the xScale need to rotate to fit. However, when the scales are fit again after the width of the left axis is determined,
// the labels do not need to rotate. Previously, the chart was too small because the chartArea did not expand to take up the space freed up
// due to the lack of label rotation
it('should fit scales that overlap the chart area', function() {
var chartInstance = {
@@ -337,13 +337,13 @@ describe('Test the layout service', function() {
left: 60,
right: 250,
top: 54.495963211660246,
bottom: 80.0664716027288,
bottom: 83.6977778440511
});
// Are xScales at the right spot
expect(xScale1.left).toBe(60);
expect(xScale1.right).toBe(250);
expect(xScale1.top).toBeCloseTo(80.06, 1e-3);
expect(xScale1.top).toBeCloseTo(83.69, 1e-3);
expect(xScale1.bottom).toBe(150);
expect(xScale2.left).toBe(0);
@@ -355,6 +355,6 @@ describe('Test the layout service', function() {
expect(yScale.left).toBe(0);
expect(yScale.right).toBe(60);
expect(yScale.top).toBeCloseTo(54.49, 1e-3);
expect(yScale.bottom).toBeCloseTo(80.06, 1e-3);
expect(yScale.bottom).toBeCloseTo(83.69, 1e-3);
});
});
});

View File

@@ -20,23 +20,23 @@ describe('Category scale tests', function() {
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
zeroLineWidth: 1
},
position: "bottom",
scaleLabel: {
labelString: '',
display: false,
display: false
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
}
});
@@ -93,7 +93,7 @@ describe('Category scale tests', function() {
});
scale.buildTicks();
expect(scale.getLabelForIndex(1)).toBe('tick2');
});
@@ -340,4 +340,4 @@ describe('Category scale tests', function() {
expect(scale.getPixelForValue(0, 3, 0, false)).toBe(199);
expect(scale.getPixelForValue(0, 3, 0, true)).toBe(199);
});
});
});

View File

@@ -28,14 +28,14 @@ describe('Linear Scale', function() {
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this work nicer, then check below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
}
});
@@ -2049,4 +2049,4 @@ describe('Linear Scale', function() {
"args": []
}])
});
});
});

View File

@@ -27,14 +27,14 @@ describe('Logarithmic Scale tests', function() {
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
},
});
@@ -626,4 +626,4 @@ describe('Logarithmic Scale tests', function() {
expect(horizontalScale.getPixelForValue(10, 0, 0)).toBeCloseTo(57.5, 1e-4); // halfway
expect(horizontalScale.getPixelForValue(0, 0, 0)).toBe(5); // 0 is invalid, put it on the left.
});
});
});

View File

@@ -41,7 +41,7 @@ describe('Test the radial linear scale', function() {
backdropPaddingY: 2,
backdropPaddingX: 2,
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
@@ -49,7 +49,7 @@ describe('Test the radial linear scale', function() {
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
autoSkipPadding: 0
},
});

View File

@@ -44,23 +44,23 @@ describe('Time scale tests', function() {
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
zeroLineWidth: 1
},
position: "bottom",
scaleLabel: {
labelString: '',
display: false,
display: false
},
ticks: {
beginAtZero: false,
maxRotation: 90,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below,
autoSkip: false,
autoSkipPadding: 20
autoSkipPadding: 0
},
time: {
parser: false,
@@ -77,8 +77,8 @@ describe('Time scale tests', function() {
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
'month': 'MMM YYYY', // Sept 2015
'quarter': '[Q]Q - YYYY', // Q3
'year': 'YYYY', // 2015
},
'year': 'YYYY' // 2015
}
}
});
@@ -108,7 +108,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015']);
expect(scale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 6, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks using date objects', function() {
@@ -136,7 +136,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015']);
expect(scale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 6, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks when the data is xy points', function() {
@@ -187,7 +187,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015']);
expect(scale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 6, 2015', 'Jan 11, 2015' ]);
});
it('should allow custom time parsers', function() {
@@ -302,7 +302,7 @@ describe('Time scale tests', function() {
});
scale.update(400, 50);
expect(scale.ticks).toEqual(['Jan 1, 4AM', 'Jan 1, 4PM', 'Jan 2, 4AM', 'Jan 2, 4PM', 'Jan 3, 4AM', 'Jan 3, 4PM', 'Jan 4, 4AM', 'Jan 4, 4PM', 'Jan 5, 4AM', 'Jan 5, 6AM']);
expect(scale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015' ]);
});
it('should get the correct pixel for a value', function() {
@@ -329,14 +329,14 @@ describe('Time scale tests', function() {
scale.update(400, 50);
expect(scale.width).toBe(400);
expect(scale.height).toBe(50);
expect(scale.height).toBe(28);
scale.left = 0;
scale.right = 400;
scale.top = 10;
scale.bottom = 38;
expect(scale.getPixelForValue('', 0, 0)).toBe(128);
expect(scale.getPixelForValue('', 6, 0)).toBe(380);
expect(scale.getPixelForValue('', 0, 0)).toBe(81);
expect(scale.getPixelForValue('', 6, 0)).toBe(323);
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
verticalScaleConfig.position = "left";
@@ -385,7 +385,7 @@ describe('Time scale tests', function() {
scale.update(400, 50);
expect(scale.width).toBe(400);
expect(scale.height).toBe(50);
expect(scale.height).toBe(28);
scale.left = 0;
scale.right = 400;
scale.top = 10;