From ee383ef4a68abc23353108204bf89409f4b8208d Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sun, 8 May 2016 09:24:45 -0400 Subject: [PATCH] Logarithmic scale improvements --- src/scales/scale.logarithmic.js | 164 ++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 70 deletions(-) diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 408318f92..4cdc0ac36 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -10,7 +10,7 @@ module.exports = function(Chart) { // label settings ticks: { callback: function(value, index, arr) { - var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value)))); + var remain = value / (Math.pow(10, Math.floor(helpers.log10(value)))); if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) { return value.toExponential(); @@ -23,99 +23,116 @@ module.exports = function(Chart) { var LogarithmicScale = Chart.Scale.extend({ determineDataLimits: function() { - // Calculate Range - this.min = null; - this.max = null; + var _this = this; + var opts = _this.options; + var tickOpts = opts.ticks; + var chart = _this.chart; + var data = chart.data; + var datasets = data.datasets; + var getValueOrDefault = helpers.getValueOrDefault; + var isHorizontal = _this.isHorizontal(); + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === _this.id : meta.yAxisID === _this.id; + } - if (this.options.stacked) { + // Calculate Range + _this.min = null; + _this.max = null; + + if (opts.stacked) { var valuesPerType = {}; - helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) { - var meta = this.chart.getDatasetMeta(datasetIndex); - if (this.chart.isDatasetVisible(datasetIndex) && (this.isHorizontal() ? meta.xAxisID === this.id : meta.yAxisID === this.id)) { + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { if (valuesPerType[meta.type] === undefined) { valuesPerType[meta.type] = []; } helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerType[meta.type]; - var value = +this.getRightValue(rawValue); + var value = +_this.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } values[index] = values[index] || 0; - if (this.options.relativePoints) { + if (opts.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); + }); helpers.each(valuesPerType, function(valuesForType) { var minVal = helpers.min(valuesForType); var maxVal = helpers.max(valuesForType); - this.min = this.min === null ? minVal : Math.min(this.min, minVal); - this.max = this.max === null ? maxVal : Math.max(this.max, maxVal); - }, this); + _this.min = _this.min === null ? minVal : Math.min(_this.min, minVal); + _this.max = _this.max === null ? maxVal : Math.max(_this.max, maxVal); + }); } else { - helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) { - var meta = this.chart.getDatasetMeta(datasetIndex); - if (this.chart.isDatasetVisible(datasetIndex) && (this.isHorizontal() ? meta.xAxisID === this.id : meta.yAxisID === this.id)) { + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +this.getRightValue(rawValue); + var value = +_this.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } - if (this.min === null) { - this.min = value; - } else if (value < this.min) { - this.min = value; + 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; + if (_this.max === null) { + _this.max = value; + } else if (value > _this.max) { + _this.max = value; } - }, this); + }); } - }, this); + }); } - this.min = this.options.ticks.min !== undefined ? this.options.ticks.min : this.min; - this.max = this.options.ticks.max !== undefined ? this.options.ticks.max : this.max; + _this.min = getValueOrDefault(tickOpts.min, _this.min); + _this.max = getValueOrDefault(tickOpts.max, _this.max); - 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); + 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; + _this.min = 1; + _this.max = 10; } } }, buildTicks: function() { + var _this = this; + var opts = _this.options; + var tickOpts = opts.ticks; + var getValueOrDefault = helpers.getValueOrDefault; + // 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 = []; + var ticks = _this.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 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph - var tickVal = this.options.ticks.min !== undefined ? this.options.ticks.min : Math.pow(10, Math.floor(helpers.log10(this.min))); + var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(_this.min)))); - while (tickVal < this.max) { - this.ticks.push(tickVal); + while (tickVal < _this.max) { + ticks.push(tickVal); var exp = Math.floor(helpers.log10(tickVal)); var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1; @@ -128,27 +145,27 @@ module.exports = function(Chart) { tickVal = significand * Math.pow(10, exp); } - var lastTick = this.options.ticks.max !== undefined ? this.options.ticks.max : tickVal; - this.ticks.push(lastTick); + var lastTick = getValueOrDefault(tickOpts.max, tickVal); + ticks.push(lastTick); - if (this.options.position === "left" || this.options.position === "right") { + if (!_this.isHorizontal()) { // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); + ticks.reverse(); } // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale - this.max = helpers.max(this.ticks); - this.min = helpers.min(this.ticks); + _this.max = helpers.max(ticks); + _this.min = helpers.min(ticks); - if (this.options.ticks.reverse) { - this.ticks.reverse(); + if (tickOpts.reverse) { + ticks.reverse(); - this.start = this.max; - this.end = this.min; + _this.start = _this.max; + _this.end = _this.min; } else { - this.start = this.min; - this.end = this.max; + _this.start = _this.min; + _this.end = _this.max; } }, convertTicksToLabels: function() { @@ -164,48 +181,55 @@ module.exports = function(Chart) { return this.getPixelForValue(this.tickValues[index], null, null, includeOffset); }, getPixelForValue: function(value, index, datasetIndex, includeOffset) { + var _this = this; + var innerDimension; var pixel; - var newVal = +this.getRightValue(value) -; var range = helpers.log10(this.end) - helpers.log10(this.start); + var start = _this.start; + var newVal = +_this.getRightValue(value); + var range = helpers.log10(_this.end) - helpers.log10(start); + var paddingTop = _this.paddingTop; + var paddingBottom = _this.paddingBottom; + var paddingLeft = _this.paddingLeft; - if (this.isHorizontal()) { + if (_this.isHorizontal()) { if (newVal === 0) { - pixel = this.left + this.paddingLeft; + pixel = _this.left + paddingLeft; } else { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - pixel = this.left + (innerWidth / range * (helpers.log10(newVal) - helpers.log10(this.start))); - pixel += this.paddingLeft; + innerDimension = _this.width - (paddingLeft + _this.paddingRight); + pixel = _this.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); + pixel += paddingLeft; } } else { // Bottom - top since pixels increase downard on a screen if (newVal === 0) { - pixel = this.top + this.paddingTop; + pixel = _this.top + paddingTop; } else { - var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (helpers.log10(newVal) - helpers.log10(this.start))); + innerDimension = _this.height - (paddingTop + paddingBottom); + pixel = (_this.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); } } return pixel; }, getValueForPixel: function(pixel) { + var _this = this; var offset; - var range = helpers.log10(this.end) - helpers.log10(this.start); + var range = helpers.log10(_this.end) - helpers.log10(_this.start); var value; + var innerDimension; - if (this.isHorizontal()) { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - value = this.start * Math.pow(10, (pixel - this.left - this.paddingLeft) * range / innerWidth); + if (_this.isHorizontal()) { + innerDimension = _this.width - (_this.paddingLeft + _this.paddingRight); + value = _this.start * Math.pow(10, (pixel - _this.left - _this.paddingLeft) * range / innerDimension); } else { - var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - value = Math.pow(10, (this.bottom - this.paddingBottom - pixel) * range / innerHeight) / this.start; + innerDimension = _this.height - (_this.paddingTop + _this.paddingBottom); + value = Math.pow(10, (_this.bottom - _this.paddingBottom - pixel) * range / innerDimension) / _this.start; } return value; } - }); Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig);