]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix some time rounding problems
authorEvert Timberg <evert.timberg@gmail.com>
Sun, 6 Mar 2016 03:58:34 +0000 (22:58 -0500)
committerEvert Timberg <evert.timberg@gmail.com>
Sun, 6 Mar 2016 03:58:34 +0000 (22:58 -0500)
docs/01-Scales.md
src/core/core.scale.js
src/scales/scale.time.js
test/scale.time.tests.js

index 58a6ad8da4215a58339dceb8140e3fa1e6158c94..df627be07e9100e326e54e33a7d06b1a5ef2cd82 100644 (file)
@@ -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: {
index 6a51b0bda286fc7d58074c8033af67e8bca4431c..538263290f3f9c28fa9f9331caebdd79f95cda6b 100644 (file)
@@ -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;
index 03563df31bbad76be1192ac79d7db90fd1de8b6a..66bd891d32ce3a37901a04d18e95081c967f38ae 100644 (file)
@@ -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 <max> 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);
index f5c3f470a3c64759fd20d8de6ce76e2a1fff526c..5cf2385c064fe22eeb59d52fa1c2482ad11be9f5 100644 (file)
@@ -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() {