From: Evert Timberg Date: Sun, 6 Mar 2016 03:58:34 +0000 (-0500) Subject: Fix some time rounding problems X-Git-Tag: v2.0.0~24^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c3f765857edf974f1136e2162e00d931bc2400bc;p=thirdparty%2FChart.js.git Fix some time rounding problems --- diff --git a/docs/01-Scales.md b/docs/01-Scales.md index 58a6ad8da..df627be07 100644 --- a/docs/01-Scales.md +++ b/docs/01-Scales.md @@ -171,8 +171,13 @@ The time scale extends the core scale class with the following tick template: parser: false, // string - By default, unit will automatically be detected. Override with 'week', 'month', 'year', etc. (see supported time measurements) unit: false, + + // Number - The number of steps of the above unit between ticks + unitStepSize: 1 + // string - By default, no rounding is applied. To round, set to a supported time unit eg. 'week', 'month', 'year', etc. round: false, + // Moment js for each of the units. Replaces `displayFormat` // To override, use a pattern string from http://momentjs.com/docs/#/displaying/format/ displayFormats: { diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 6a51b0bda..538263290 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -476,10 +476,6 @@ module.exports = function(Chart) { skipRatio = 1 + Math.floor((((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight))); } - if (!useAutoskipper) { - skipRatio = false; - } - // if they defined a max number of ticks, // increase skipRatio until that number is met if (maxTicks && this.ticks.length > maxTicks) { @@ -491,6 +487,10 @@ module.exports = function(Chart) { } } + if (!useAutoskipper) { + skipRatio = false; + } + helpers.each(this.ticks, function(label, index) { // Blank ticks var isLastTick = this.ticks.length === index + 1; diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 03563df31..66bd891d3 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -138,22 +138,30 @@ module.exports = function(Chart) { 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) // Set unit override if applicable if (this.options.time.unit) { this.tickUnit = this.options.time.unit || 'day'; this.displayFormat = this.options.time.displayFormats[this.tickUnit]; - this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true)); + this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true); + 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); - var labelCapacity = innerWidth / (tickFontSize + 10); - var buffer = this.options.time.round ? 0 : 1; + + // Crude approximation of what the label length might be + var tempFirstLabel = this.tickFormatFunction(this.firstTick, 0, []); + var tickLabelWidth = tempFirstLabel.length * tickFontSize; + 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); // Start as small as possible this.tickUnit = 'millisecond'; - this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true) + buffer); + this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true); this.displayFormat = this.options.time.displayFormats[this.tickUnit]; var unitDefinitionIndex = 0; @@ -164,19 +172,19 @@ module.exports = function(Chart) { // Can we scale this unit. If `false` we can scale infinitely this.unitScale = 1; - if (helpers.isArray(unitDefinition.steps) && Math.ceil(this.tickRange / labelCapacity) < helpers.max(unitDefinition.steps)) { + 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.tickRange / labelCapacity)) { - this.unitScale = unitDefinition.steps[idx]; + if (unitDefinition.steps[idx] > Math.ceil(this.scaleSizeInUnits / labelCapacity)) { + this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, unitDefinition.steps[idx]); break; } } break; - } else if ((unitDefinition.maxStep === false) || (Math.ceil(this.tickRange / labelCapacity) < unitDefinition.maxStep)) { + } else if ((unitDefinition.maxStep === false) || (Math.ceil(this.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) { // We have a max step. Scale this unit - this.unitScale = Math.ceil(this.tickRange / labelCapacity); + this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, Math.ceil(this.scaleSizeInUnits / labelCapacity)); break; } else { // Move to the next unit up @@ -184,7 +192,7 @@ module.exports = function(Chart) { unitDefinition = time.units[unitDefinitionIndex]; this.tickUnit = unitDefinition.name; - this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit) + buffer); + this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true); this.displayFormat = this.options.time.displayFormats[unitDefinition.name]; } } @@ -222,7 +230,7 @@ module.exports = function(Chart) { this.ticks.push(this.firstTick.clone()); // For every unit in between the first and last moment, create a moment and add it to the ticks tick - for (var i = 1; i < this.tickRange; ++i) { + for (var i = 1; i < this.scaleSizeInUnits; ++i) { var newTick = roundedStart.clone().add(i, this.tickUnit); // Are we greater than the max time @@ -236,12 +244,17 @@ module.exports = function(Chart) { } // Always show the right tick - if (this.options.time.max) { - this.ticks.push(this.lastTick.clone()); - } else if (this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit, true) !== 0) { - this.tickRange = Math.ceil(this.tickRange / this.unitScale) * this.unitScale; - this.ticks.push(this.firstTick.clone().add(this.tickRange, this.tickUnit)); - this.lastTick = this.ticks[this.ticks.length - 1].clone(); + if (this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit) !== 0 || this.scaleSizeInUnits === 0) { + // this is a weird case. If the option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart + // but the last tick was not rounded. + if (this.options.time.max) { + this.ticks.push(this.lastTick.clone()); + this.scaleSizeInUnits = this.lastTick.diff(this.ticks[0], this.tickUnit, true); + } else { + this.scaleSizeInUnits = Math.ceil(this.scaleSizeInUnits / this.unitScale) * this.unitScale; + this.ticks.push(this.firstTick.clone().add(this.scaleSizeInUnits, this.tickUnit)); + this.lastTick = this.ticks[this.ticks.length - 1].clone(); + } } }, // Get tooltip label @@ -259,16 +272,18 @@ module.exports = function(Chart) { return label; }, - convertTicksToLabels: function() { - this.ticks = this.ticks.map(function(tick, index, ticks) { - var formattedTick = tick.format(this.displayFormat); + // Function to format an individual tick mark + tickFormatFunction: function tickFormatFunction(tick, index, ticks) { + var formattedTick = tick.format(this.displayFormat); - if (this.options.ticks.userCallback) { - return this.options.ticks.userCallback(formattedTick, index, ticks); - } else { - return formattedTick; - } - }, this); + if (this.options.ticks.userCallback) { + return this.options.ticks.userCallback(formattedTick, index, ticks); + } else { + return formattedTick; + } + }, + convertTicksToLabels: function() { + this.ticks = this.ticks.map(this.tickFormatFunction, this); }, getPixelForValue: function(value, index, datasetIndex, includeOffset) { var labelMoment = this.getLabelMoment(datasetIndex, index); @@ -276,7 +291,7 @@ module.exports = function(Chart) { if (labelMoment) { var offset = labelMoment.diff(this.firstTick, this.tickUnit, true); - var decimal = offset / this.tickRange; + var decimal = offset / this.scaleSizeInUnits; if (this.isHorizontal()) { var innerWidth = this.width - (this.paddingLeft + this.paddingRight); diff --git a/test/scale.time.tests.js b/test/scale.time.tests.js index f5c3f470a..5cf2385c0 100644 --- a/test/scale.time.tests.js +++ b/test/scale.time.tests.js @@ -202,7 +202,7 @@ describe('Time scale tests', function() { scale.update(400, 50); // Counts down because the lines are drawn top to bottom - expect(scale.ticks).toEqual(['Nov 20, 1981', 'Nov 20, 1981']); + expect(scale.ticks).toEqual(['Nov 19, 1981', 'Nov 19, 1981']); }); it('should build ticks using the config unit', function() {