From: Tanner Linsley Date: Thu, 24 Sep 2015 18:07:40 +0000 (-0600) Subject: Time Scale working, troubleshooting log scale X-Git-Tag: 2.0.0-alpha4~12^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d198157fb8ea479ec9286bcf0ffb72e2e62e9661;p=thirdparty%2FChart.js.git Time Scale working, troubleshooting log scale --- diff --git a/samples/combo-time-scale.html b/samples/combo-time-scale.html index 548202e2c..6b1bfef4d 100644 --- a/samples/combo-time-scale.html +++ b/samples/combo-time-scale.html @@ -55,12 +55,14 @@ // labels: ["01/01/2015", "01/02/2015", "01/03/2015", "01/06/2015", "01/15/2015", "01/17/2015", "01/30/2015"], // Days // labels: ["12/25/2014", "01/08/2015", "01/15/2015", "01/22/2015", "01/29/2015", "02/05/2015", "02/12/2015"], // Weeks datasets: [{ + type: 'bar', label: 'Dataset 1', backgroundColor: "rgba(151,187,205,0.5)", data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()], borderColor: 'white', borderWidth: 2 }, { + type: 'bar', label: 'Dataset 2', backgroundColor: "rgba(151,187,205,0.5)", data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()], @@ -90,7 +92,7 @@ }, elements: { line: { - tension: 0 + tension: 0.3 } }, } diff --git a/samples/line-time-scale.html b/samples/line-time-scale.html index 3fc97b52b..7e238dcdf 100644 --- a/samples/line-time-scale.html +++ b/samples/line-time-scale.html @@ -89,7 +89,7 @@ }, elements: { line: { - tension: 0 + tension: 0.3 } }, } diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index bd2d5f0ee..933147c41 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -228,7 +228,13 @@ var datasetCount = !this.chart.isCombo ? this.chart.data.datasets.length : helpers.where(this.chart.data.datasets, function(ds) { return ds.type == 'bar'; }).length; - var tickWidth = xScale.getPixelForValue(null, 1) - xScale.getPixelForValue(null, 0); + var tickWidth = (function() { + var min = xScale.getPixelForValue(null, 1) - xScale.getPixelForValue(null, 0); + for (var i = 2; i < this.getDataset().data.length; i++) { + min = Math.min(xScale.getPixelForValue(null, i) - xScale.getPixelForValue(null, i - 1), min); + } + return min; + }).call(this); var categoryWidth = tickWidth * xScale.options.categoryPercentage; var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; var fullBarWidth = categoryWidth / datasetCount; @@ -265,8 +271,9 @@ var yScale = this.getScaleForID(this.getDataset().yAxisID); var xScale = this.getScaleForID(this.getDataset().xAxisID); - var leftTick = xScale.getPixelForValue(null, index); var ruler = this.getRuler(); + var leftTick = xScale.getPixelForValue(null, index, datasetIndex); + leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0; if (yScale.options.stacked) { return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 26db1cd10..578d02e83 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -211,7 +211,10 @@ buildOrUpdateControllers: function() { var types = []; helpers.each(this.data.datasets, function(dataset, datasetIndex) { - var type = dataset.type || this.config.type; + if (!dataset.type) { + dataset.type = this.config.type; + } + var type = dataset.type; types.push(type); if (dataset.controller) { dataset.controller.updateIndex(datasetIndex); diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 3071d613d..4f67ef964 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -127,7 +127,6 @@ //Max label rotation can be set or default to 90 - also act as a loop counter while (this.labelWidth > tickWidth && this.labelRotation <= this.options.ticks.maxRotation) { - console.log(this.labelWidth, tickWidth, ',', this.labelRotation, this.options.ticks.maxRotation); cosRotation = Math.cos(helpers.toRadians(this.labelRotation)); sinRotation = Math.sin(helpers.toRadians(this.labelRotation)); @@ -141,7 +140,6 @@ this.paddingRight = this.options.ticks.fontSize / 2; - console.log(sinRotation * originalLabelWidth, this.maxHeight); if (sinRotation * originalLabelWidth > this.maxHeight) { // go back one step this.labelRotation--; diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index b7e0defb5..702451493 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -6,96 +6,108 @@ helpers = Chart.helpers; var defaultConfig = { - display: true, position: "left", - // grid line settings - gridLines: { - show: true, - color: "rgba(0, 0, 0, 0.1)", - lineWidth: 1, - drawOnChartArea: true, - drawTicks: true, // draw ticks extending towards the label - zeroLineWidth: 1, - zeroLineColor: "rgba(0,0,0,0.25)", - }, - // scale label scaleLabel: { - fontColor: '#666', - fontFamily: 'Helvetica Neue', - fontSize: 12, - fontStyle: 'normal', - // actual label labelString: '', - // display property show: false, }, - // scale numbers - reverse: false, - override: null, - // label settings labels: { - show: true, - mirror: false, - padding: 10, template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>", - fontSize: 12, - fontStyle: "normal", - fontColor: "#666", - fontFamily: "Helvetica Neue" } }; - var LogarithmicScale = Chart.Element.extend({ - isHorizontal: function() { - return this.options.position == "top" || this.options.position == "bottom"; - }, - generateTicks: function(width, height) { - // We need to decide how many ticks we are going to have. Each tick draws a grid line. - // There are two possibilities. The first is that the user has manually overridden the scale - // calculations in which case the job is easy. The other case is that we have to do it ourselves - // - // We assume at this point that the scale object has been updated with the following values - // by the chart. - // min: this is the minimum value of the scale - // max: this is the maximum value of the scale - // options: contains the options for the scale. This is referenced from the user settings - // rather than being cloned. This ensures that updates always propogate to a redraw + var LogarithmicScale = Chart.Scale.extend({ + buildTicks: function() { - // Reset the ticks array. Later on, we will draw a grid line at these positions - // The array simply contains the numerical value of the spots where ticks will be - this.ticks = []; + // Calculate Range (we may break this out into it's own lifecycle function) + + this.min = null; + this.max = null; + + var values = []; + + if (this.options.stacked) { + helpers.each(this.data.datasets, function(dataset) { + if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) { + helpers.each(dataset.data, function(rawValue, index) { + + var value = this.getRightValue(rawValue); + + values[index] = values[index] || 0; + + if (this.options.relativePoints) { + values[index] = 100; + } else { + // Don't need to split positive and negative since the log scale can't handle a 0 crossing + values[index] += value; + } + }, this); + } + }, this); + + this.min = helpers.min(values); + this.max = helpers.max(values); - if (this.options.override) { - // The user has specified the manual override. We use <= instead of < so that - // we get the final line - for (var i = 0; i <= this.options.override.steps; ++i) { - var value = this.options.override.start + (i * this.options.override.stepWidth); - this.ticks.push(value); - } } else { - // 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 + helpers.each(this.data.datasets, function(dataset) { + if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) { + helpers.each(dataset.data, function(rawValue, index) { + var value = this.getRightValue(rawValue); - var minExponent = Math.floor(helpers.log10(this.min)); - var maxExponent = Math.ceil(helpers.log10(this.max)); + if (this.min === null) { + this.min = value; + } else if (value < this.min) { + this.min = value; + } - for (var exponent = minExponent; exponent < maxExponent; ++exponent) { - for (var i = 1; i < 10; ++i) { - this.ticks.push(i * Math.pow(10, exponent)); + if (this.max === null) { + this.max = value; + } else if (value > this.max) { + this.max = value; + } + }, this); } + }, this); + } + + if (this.min === this.max) { + if (this.min !== 0 && this.min !== null) { + this.min = Math.pow(10, Math.floor(helpers.log10(this.min)) - 1); + this.max = Math.pow(10, Math.floor(helpers.log10(this.max)) + 1); + } else { + this.min = 1; + this.max = 10; } + } + + + // Reset the ticks array. Later on, we will draw a grid line at these positions + // The array simply contains the numerical value of the spots where ticks will be + this.ticks = []; - this.ticks.push(1.0 * Math.pow(10, maxExponent)); + + // 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 minExponent = Math.floor(helpers.log10(this.min)); + var maxExponent = Math.ceil(helpers.log10(this.max)); + + for (var exponent = minExponent; exponent < maxExponent; ++exponent) { + for (var i = 1; i < 10; ++i) { + this.ticks.push(i * Math.pow(10, exponent)); + } } + this.ticks.push(1.0 * Math.pow(10, maxExponent)); + if (this.options.position == "left" || this.options.position == "right") { // We are in a vertical orientation. The top value is the highest. So reverse the array this.ticks.reverse(); @@ -115,6 +127,8 @@ this.start = this.min; this.end = this.max; } + + console.log(this.ticks); }, buildLabels: function() { // We assume that this has been run after ticks have been generated. We try to figure out @@ -159,7 +173,7 @@ // Bottom - top since pixels increase downard on a screen if (value === 0) { pixel = this.top + this.paddingTop; - } else { + } else { var innerHeight = this.height - (this.paddingTop + this.paddingBottom); pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (helpers.log10(value) - helpers.log10(this.start))); } @@ -168,68 +182,6 @@ return pixel; }, - // Functions needed for line charts - calculateRange: function() { - this.min = null; - this.max = null; - - var values = []; - - if (this.options.stacked) { - helpers.each(this.data.datasets, function(dataset) { - if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) { - helpers.each(dataset.data, function(rawValue, index) { - - var value = this.getRightValue(rawValue); - - values[index] = values[index] || 0; - - if (this.options.relativePoints) { - values[index] = 100; - } else { - // Don't need to split positive and negative since the log scale can't handle a 0 crossing - values[index] += value; - } - }, this); - } - }, this); - - this.min = helpers.min(values); - this.max = helpers.max(values); - - } else { - helpers.each(this.data.datasets, function(dataset) { - if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) { - helpers.each(dataset.data, function(rawValue, index) { - var value = this.getRightValue(rawValue); - - if (this.min === null) { - this.min = value; - } else if (value < this.min) { - this.min = value; - } - - if (this.max === null) { - this.max = value; - } else if (value > this.max) { - this.max = value; - } - }, this); - } - }, this); - } - - if (this.min === this.max) { - if (this.min !== 0 && this.min !== null) { - this.min = Math.pow(10, Math.floor(helpers.log10(this.min)) - 1); - this.max = Math.pow(10, Math.floor(helpers.log10(this.max)) + 1); - } else { - this.min = 1; - this.max = 10; - } - } - }, - getPointPixelForValue: function(rawValue, index, datasetIndex) { var value = this.getRightValue(rawValue); @@ -319,321 +271,6 @@ return this.getPixelForValue(value); }, - - // Fit this axis to the given size - // @param {number} maxWidth : the max width the axis can be - // @param {number} maxHeight: the max height the axis can be - // @return {object} minSize : the minimum size needed to draw the axis - fit: function(maxWidth, maxHeight, margins) { - this.calculateRange(); - this.generateTicks(maxWidth, maxHeight); - this.buildLabels(); - - var minSize = { - width: 0, - height: 0, - }; - - // In a horizontal axis, we need some room for the scale to be drawn - // - // ----------------------------------------------------- - // | | | | | - // - // In a vertical axis, we need some room for the scale to be drawn. - // The actual grid lines will be drawn on the chart area, however, we need to show - // ticks where the axis actually is. - // We will allocate 25px for this width - // | - // -| - // | - // | - // -| - // | - // | - // -| - - - // Width - if (this.isHorizontal()) { - minSize.width = maxWidth; // fill all the width - } else { - minSize.width = this.options.gridLines.show && this.options.display ? 10 : 0; - } - - // height - if (this.isHorizontal()) { - minSize.height = this.options.gridLines.show && this.options.display ? 10 : 0; - } else { - minSize.height = maxHeight; // fill all the height - } - - // Are we showing a label for the scale? - if (this.options.scaleLabel.show) { - if (this.isHorizontal()) { - minSize.height += (this.options.scaleLabel.fontSize * 1.5); - } else { - minSize.width += (this.options.scaleLabel.fontSize * 1.5); - } - } - - this.paddingLeft = 0; - this.paddingRight = 0; - this.paddingTop = 0; - this.paddingBottom = 0; - - - if (this.options.labels.show && this.options.display) { - // Don't bother fitting the labels if we are not showing them - var labelFont = helpers.fontString(this.options.labels.fontSize, - this.options.labels.fontStyle, this.options.labels.fontFamily); - - if (this.isHorizontal()) { - // A horizontal axis is more constrained by the height. - var maxLabelHeight = maxHeight - minSize.height; - var labelHeight = 1.5 * this.options.labels.fontSize; - minSize.height = Math.min(maxHeight, minSize.height + labelHeight); - - var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily); - this.ctx.font = labelFont; - - var firstLabelWidth = this.ctx.measureText(this.labels[0]).width; - var lastLabelWidth = this.ctx.measureText(this.labels[this.labels.length - 1]).width; - - // Ensure that our labels are always inside the canvas - this.paddingLeft = firstLabelWidth / 2; - this.paddingRight = lastLabelWidth / 2; - } else { - // A vertical axis is more constrained by the width. Labels are the dominant factor - // here, so get that length first - var maxLabelWidth = maxWidth - minSize.width; - var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.labels); - - if (largestTextWidth < maxLabelWidth) { - // We don't need all the room - minSize.width += largestTextWidth; - minSize.width += 3; // extra padding - } else { - // Expand to max size - minSize.width = maxWidth; - } - - this.paddingTop = this.options.labels.fontSize / 2; - this.paddingBottom = this.options.labels.fontSize / 2; - } - } - - if (margins) { - this.paddingLeft -= margins.left; - this.paddingTop -= margins.top; - this.paddingRight -= margins.right; - this.paddingBottom -= margins.bottom; - - this.paddingLeft = Math.max(this.paddingLeft, 0); - this.paddingTop = Math.max(this.paddingTop, 0); - this.paddingRight = Math.max(this.paddingRight, 0); - this.paddingBottom = Math.max(this.paddingBottom, 0); - } - - this.width = minSize.width; - this.height = minSize.height; - return minSize; - }, - // Actualy draw the scale on the canvas - // @param {rectangle} chartArea : the area of the chart to draw full grid lines on - draw: function(chartArea) { - if (this.options.display) { - - var setContextLineSettings; - var hasZero; - - // Make sure we draw text in the correct color - this.ctx.fillStyle = this.options.labels.fontColor; - - if (this.isHorizontal()) { - if (this.options.gridLines.show) { - // Draw the horizontal line - setContextLineSettings = true; - hasZero = helpers.findNextWhere(this.ticks, function(tick) { - return tick === 0; - }) !== undefined; - var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 5; - var yTickEnd = this.options.position == "bottom" ? this.top + 5 : this.bottom; - - helpers.each(this.ticks, function(tick, index) { - // Grid lines are vertical - var xValue = this.getPixelForValue(tick); - - if (this.labels[index] === null) { - // If the user specifically hid the label by returning null from the label function, do so - return; - } - - if (tick === 0 || (!hasZero && index === 0)) { - // Draw the 0 point specially or the left if there is no 0 - this.ctx.lineWidth = this.options.gridLines.zeroLineWidth; - this.ctx.strokeStyle = this.options.gridLines.zeroLineColor; - setContextLineSettings = true; // reset next time - } else if (setContextLineSettings) { - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - setContextLineSettings = false; - } - - xValue += helpers.aliasPixel(this.ctx.lineWidth); - - // Draw the label area - this.ctx.beginPath(); - - if (this.options.gridLines.drawTicks) { - this.ctx.moveTo(xValue, yTickStart); - this.ctx.lineTo(xValue, yTickEnd); - } - - // Draw the chart area - if (this.options.gridLines.drawOnChartArea) { - this.ctx.moveTo(xValue, chartArea.top); - this.ctx.lineTo(xValue, chartArea.bottom); - } - - // Need to stroke in the loop because we are potentially changing line widths & colours - this.ctx.stroke(); - }, this); - } - - if (this.options.labels.show) { - // Draw the labels - - var labelStartY; - - if (this.options.position == "top") { - labelStartY = this.bottom - 10; - this.ctx.textBaseline = "bottom"; - } else { - // bottom side - labelStartY = this.top + 10; - this.ctx.textBaseline = "top"; - } - - this.ctx.textAlign = "center"; - this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily); - - helpers.each(this.labels, function(label, index) { - var xValue = this.getPixelForValue(this.ticks[index]); - if (label) { - this.ctx.fillText(label, xValue, labelStartY); - } - }, this); - } - - if (this.options.scaleLabel.show) { - // Draw the scale label - this.ctx.textAlign = "center"; - this.ctx.textBaseline = 'middle'; - this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily); - - var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width - var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2); - - this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY); - } - } else { - // Vertical - if (this.options.gridLines.show) { - - // Draw the vertical line - setContextLineSettings = true; - hasZero = helpers.findNextWhere(this.ticks, function(tick) { - return tick === 0; - }) !== undefined; - var xTickStart = this.options.position == "right" ? this.left : this.right - 5; - var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right; - - helpers.each(this.ticks, function(tick, index) { - // Grid lines are horizontal - var yValue = this.getPixelForValue(tick); - - if (tick === 0 || (!hasZero && index === 0)) { - // Draw the 0 point specially or the bottom if there is no 0 - this.ctx.lineWidth = this.options.gridLines.zeroLineWidth; - this.ctx.strokeStyle = this.options.gridLines.zeroLineColor; - setContextLineSettings = true; // reset next time - } else if (setContextLineSettings) { - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - setContextLineSettings = false; // use boolean to indicate that we only want to do this once - } - - yValue += helpers.aliasPixel(this.ctx.lineWidth); - - // Draw the label area - this.ctx.beginPath(); - - if (this.options.gridLines.drawTicks) { - this.ctx.moveTo(xTickStart, yValue); - this.ctx.lineTo(xTickEnd, yValue); - } - - // Draw the chart area - if (this.options.gridLines.drawOnChartArea) { - this.ctx.moveTo(chartArea.left, yValue); - this.ctx.lineTo(chartArea.right, yValue); - } - - this.ctx.stroke(); - }, this); - } - - if (this.options.labels.show) { - // Draw the labels - - var labelStartX; - - if (this.options.position == "left") { - if (this.options.labels.mirror) { - labelStartX = this.right + this.options.labels.padding; - this.ctx.textAlign = "left"; - } else { - labelStartX = this.right - this.options.labels.padding; - this.ctx.textAlign = "right"; - } - } else { - // right side - if (this.options.labels.mirror) { - labelStartX = this.left - this.options.labels.padding; - this.ctx.textAlign = "right"; - } else { - labelStartX = this.left + this.options.labels.padding; - this.ctx.textAlign = "left"; - } - } - - this.ctx.textBaseline = "middle"; - this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily); - - helpers.each(this.labels, function(label, index) { - var yValue = this.getPixelForValue(this.ticks[index]); - this.ctx.fillText(label, labelStartX, yValue); - }, this); - } - - if (this.options.scaleLabel.show) { - // Draw the scale label - var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2); - var scaleLabelY = this.top + ((this.bottom - this.top) / 2); - var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI; - - this.ctx.save(); - this.ctx.translate(scaleLabelX, scaleLabelY); - this.ctx.rotate(rotation); - this.ctx.textAlign = "center"; - this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily); - this.ctx.textBaseline = 'middle'; - this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0); - this.ctx.restore(); - } - } - } - } }); Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig); diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 26cb67739..912d12099 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -69,23 +69,7 @@ }; var TimeScale = Chart.Scale.extend({ - parseTime: function(label) { - // Date objects - if (typeof label.getMonth === 'function' || typeof label == 'number') { - return moment(label); - } - // Moment support - if (label.isValid && label.isValid()) { - return label; - } - // Custom parsing (return an instance of moment) - if (typeof this.options.time.format !== 'string' && this.options.time.format.call) { - return this.options.time.format(label); - } - // Moment format parsing - return moment(label, this.options.time.format); - }, - generateTicks: function(index) { + buildTicks: function(index) { this.ticks = []; this.labelMoments = []; @@ -163,14 +147,11 @@ } } }, - getSmallestDataDistance: function() { - return this.smallestLabelSeparation; - }, - getPixelForValue: function(value, datasetIndex, includeOffset) { - // This must be called after fit has been run so that - // this.left, this.top, this.right, and this.bottom have been defined + getPixelForValue: function(value, index, datasetIndex, includeOffset) { + + var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true); - var decimal = 0.5; + var decimal = offset / this.tickRange; if (this.isHorizontal()) { var innerWidth = this.width - (this.paddingLeft + this.paddingRight); @@ -182,242 +163,22 @@ return this.top + (decimal * (this.height / this.ticks.length)); } }, - // getPointPixelForValue: function(value, index, datasetIndex) { - - // var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true); - // return this.getPixelForValue(value, offset / this.tickRange, datasetIndex); - // }, - - // // Functions needed for bar charts - // calculateBaseWidth: function() { - - // var base = this.getPixelForValue(null, this.smallestLabelSeparation / this.tickRange, 0, true) - this.getPixelForValue(null, 0, 0, true); - // var spacing = 2 * this.options.categorySpacing; - // if (base < spacing * 2) { - // var mod = Math.min((spacing * 2) / base, 1.5); - // base = (base / 2) * mod; - // return base; - // } - // return base - spacing; - // }, - // calculateBarWidth: function(barDatasetCount) { - // //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset - // var baseWidth = this.calculateBaseWidth() - ((barDatasetCount - 1) * this.options.spacing); - - // if (this.options.stacked) { - // return Math.max(baseWidth, 1); - // } - // return Math.max((baseWidth / barDatasetCount), 1); - // }, - // calculateBarX: function(barDatasetCount, datasetIndex, elementIndex) { - - // var xWidth = this.calculateBaseWidth(), - // offset = this.labelMoments[elementIndex].diff(this.firstTick, this.tickUnit, true), - // xAbsolute = this.getPixelForValue(null, offset / this.tickRange, datasetIndex, true) - (xWidth / 2), - // barWidth = this.calculateBarWidth(barDatasetCount); - - // if (this.options.stacked) { - // return xAbsolute + barWidth / 2; - // } - - // return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2; - // }, - - // calculateTickRotation: function(maxHeight, margins) { - // //Get the width of each grid by calculating the difference - // //between x offsets between 0 and 1. - // var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily); - // this.ctx.font = labelFont; - - // var firstWidth = this.ctx.measureText(this.ticks[0]).width; - // var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width; - // var firstRotated; - // var lastRotated; - - // this.paddingRight = lastWidth / 2 + 3; - // this.paddingLeft = firstWidth / 2 + 3; - - // this.labelRotation = 0; - - // if (this.options.display) { - // var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks); - // var cosRotation; - // var sinRotation; - // var firstRotatedWidth; - - // this.labelWidth = originalLabelWidth; - - // //Allow 3 pixels x2 padding either side for label readability - // // only the index matters for a dataset scale, but we want a consistent interface between scales - - // var datasetWidth = Math.floor(this.getPixelForValue(null, 1 / this.ticks.length) - this.getPixelForValue(null, 0)) - 6; - - // //Max label rotation can be set or default to 90 - also act as a loop counter - // while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.ticks.maxRotation) { - // cosRotation = Math.cos(helpers.toRadians(this.labelRotation)); - // sinRotation = Math.sin(helpers.toRadians(this.labelRotation)); - - // firstRotated = cosRotation * firstWidth; - // lastRotated = cosRotation * lastWidth; - - // // We're right aligning the text now. - // if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) { - // this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2; - // } - - // this.paddingRight = this.options.ticks.fontSize / 2; - - // if (sinRotation * originalLabelWidth > maxHeight) { - // // go back one step - // this.labelRotation--; - // break; - // } - - // this.labelRotation++; - // this.labelWidth = cosRotation * originalLabelWidth; - - - // } - // } else { - // this.labelWidth = 0; - // this.paddingRight = 0; - // this.paddingLeft = 0; - // } - - // if (margins) { - // this.paddingLeft -= margins.left; - // this.paddingRight -= margins.right; - - // this.paddingLeft = Math.max(this.paddingLeft, 0); - // this.paddingRight = Math.max(this.paddingRight, 0); - // } - - // }, - // Fit this axis to the given size - // @param {number} maxWidth : the max width the axis can be - // @param {number} maxHeight: the max height the axis can be - // @return {object} minSize : the minimum size needed to draw the axis - fit: function(maxWidth, maxHeight, margins) { - // Set the unconstrained dimension before label rotation - if (this.isHorizontal()) { - this.width = maxWidth; - } else { - this.height = maxHeight; + parseTime: function(label) { + // Date objects + if (typeof label.getMonth === 'function' || typeof label == 'number') { + return moment(label); } - - this.generateTicks(); - this.calculateTickRotation(maxHeight, margins); - - var minSize = { - width: 0, - height: 0, - }; - - var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily); - var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks); - - // Width - if (this.isHorizontal()) { - minSize.width = maxWidth; - } else if (this.options.display) { - var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0; - minSize.width = Math.min(labelWidth, maxWidth); + // Moment support + if (label.isValid && label.isValid()) { + return label; } - - // Height - if (this.isHorizontal() && this.options.display) { - var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize; - minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, maxHeight); - } else if (this.options.display) { - minSize.height = maxHeight; + // Custom parsing (return an instance of moment) + if (typeof this.options.time.format !== 'string' && this.options.time.format.call) { + return this.options.time.format(label); } - - this.width = minSize.width; - this.height = minSize.height; - return minSize; + // Moment format parsing + return moment(label, this.options.time.format); }, - // Actualy draw the scale on the canvas - // @param {rectangle} chartArea : the area of the chart to draw full grid lines on - draw: function(chartArea) { - if (this.options.display) { - - var setContextLineSettings; - - // Make sure we draw text in the correct color - this.ctx.fillStyle = this.options.ticks.fontColor; - - if (this.isHorizontal()) { - setContextLineSettings = true; - var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10; - var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom; - var isRotated = this.labelRotation !== 0; - var skipRatio = false; - - if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) { - skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight))); - } - - helpers.each(this.ticks, function(tick, index) { - // Blank ticks - if ((skipRatio > 1 && index % skipRatio > 0) || (tick === undefined || tick === null)) { - return; - } - var xLineValue = this.getPixelForValue(null, (1 / (this.ticks.length - 1)) * index, null, false); // xvalues for grid lines - var xLabelValue = this.getPixelForValue(null, (1 / (this.ticks.length - 1)) * index, null, true); // x values for ticks (need to consider offsetLabel option) - - if (this.options.gridLines.show) { - if (index === 0) { - // Draw the first index specially - this.ctx.lineWidth = this.options.gridLines.zeroLineWidth; - this.ctx.strokeStyle = this.options.gridLines.zeroLineColor; - setContextLineSettings = true; // reset next time - } else if (setContextLineSettings) { - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - setContextLineSettings = false; - } - - xLineValue += helpers.aliasPixel(this.ctx.lineWidth); - - // Draw the tick area - this.ctx.beginPath(); - - if (this.options.gridLines.drawTicks) { - this.ctx.moveTo(xLineValue, yTickStart); - this.ctx.lineTo(xLineValue, yTickEnd); - } - - // Draw the chart area - if (this.options.gridLines.drawOnChartArea) { - this.ctx.moveTo(xLineValue, chartArea.top); - this.ctx.lineTo(xLineValue, chartArea.bottom); - } - - // Need to stroke in the loop because we are potentially changing line widths & colours - this.ctx.stroke(); - } - - if (this.options.ticks.show) { - this.ctx.save(); - this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8); - this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1); - this.ctx.font = this.font; - this.ctx.textAlign = (isRotated) ? "right" : "center"; - this.ctx.textBaseline = (isRotated) ? "middle" : "top"; - this.ctx.fillText(tick, 0, 0); - this.ctx.restore(); - } - }, this); - } else { - // Vertical - if (this.options.gridLines.show) {} - - if (this.options.ticks.show) { - // Draw the ticks - } - } - } - } }); Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);