From 2b2dee47a96416301f4d01c66c7e447227b71095 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Tue, 22 Jan 2019 21:41:32 +0200 Subject: [PATCH 01/16] parsing + usage in category axis --- src/controllers/controller.doughnut.js | 2 + src/controllers/controller.polarArea.js | 9 +++++ src/controllers/controller.radar.js | 9 +++++ src/core/core.datasetController.js | 54 +++++++++++++++++++++++++ src/core/core.scale.js | 23 +++++++++++ src/scales/scale.category.js | 25 ++++-------- src/scales/scale.linearbase.js | 4 ++ src/scales/scale.logarithmic.js | 4 ++ src/scales/scale.time.js | 4 ++ 9 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index a6d4a63775a..c207356c39e 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -127,6 +127,8 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + _parse: helpers.noop, + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 19ceefddab7..836446860d7 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -9,6 +9,7 @@ var resolve = helpers.options.resolve; defaults._set('polarArea', { scale: { + id: 'radial', type: 'radialLinear', angleLines: { display: false @@ -116,6 +117,14 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + _getIndexScaleId: function() { + return 'radial'; + }, + + _getValueScaleId: function() { + return 'radial'; + }, + update: function(reset) { var me = this; var dataset = me.getDataset(); diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 770ddc51afe..db9a719438d 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -10,6 +10,7 @@ var resolve = helpers.options.resolve; defaults._set('radar', { scale: { + id: 'radial', type: 'radialLinear' }, elements: { @@ -27,6 +28,14 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + _getIndexScaleId: function() { + return 'radial'; + }, + + _getValueScaleId: function() { + return 'radial'; + }, + update: function(reset) { var me = this; var meta = me.getMeta(); diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index afd021c1db1..b67fa9637dc 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -203,6 +203,7 @@ helpers.extend(DatasetController.prototype, { } meta.dataset = meta.dataset || me.createMetaDataset(); + me._parse(0, ilen); }, addElementAndReset: function(index) { @@ -315,6 +316,59 @@ helpers.extend(DatasetController.prototype, { for (var i = 0; i < count; ++i) { this.addElementAndReset(start + i); } + this._parse(start, count); + }, + + /** + * @private + * parse data to internal representation utilizing scales + */ + _parse: function(start, count) { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var xID = meta.xAxisID; + var yID = meta.yAxisID; + var indexScale = me._getIndexScale(); + var valueScale = me._getValueScale(); + var xScale = me.getScaleForId(xID); + var yScale = me.getScaleForId(yID); + var labels = indexScale._getLabels() || []; + var i, ilen, j, jlen, v, key, keys, metaData; + + for (i = start, ilen = start + count; i < ilen; ++i) { + metaData = meta.data[i]; + v = data[i]; + if (helpers.isArray(v) && v.length === 2) { + metaData[xID] = xScale.parse(v[0]); + metaData[yID] = yScale.parse(v[1]); + } else if (helpers.isObject(v)) { + keys = Object.keys(v); + metaData[xID] = null; + metaData[yID] = null; + for (j = 0, jlen = keys.length; j < jlen; ++j) { + key = keys[j]; + if (key === 'x') { + metaData[xID] = xScale.parse(v.x); + } else if (key === 'y') { + metaData[yID] = yScale.parse(v.y); + } + } + if (v.t) { + if (metaData[xID] === null) { + metaData[xID] = xScale.parse(v.t); + } + if (metaData[yID] === null) { + metaData[yID] = xScale.parse(v.t); + } + } + } else { + metaData[valueScale.id] = valueScale.parse(v); + if (indexScale !== valueScale && i < labels.length) { + metaData[indexScale.id] = indexScale.parse(labels[i]); + } + } + } }, /** diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 1f65bc564b6..483b7ab0bac 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -100,6 +100,25 @@ function computeTextSize(context, tick, font) { } module.exports = Element.extend({ + /** + * Function that parses a supported input value to internal representation. + * @param {*} raw + * @since 2.9 + */ + parse: function(raw) { + return raw; + }, + + /** + * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those + * else fall back to data.labels + * @private + */ + _getLabels: function() { + var data = this.chart.data; + return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; + }, + /** * Get the padding needed for the scale * @method getPadding @@ -538,6 +557,10 @@ module.exports = Element.extend({ return rawValue; }, + _getRawValue: function(index, datasetIndex) { + return this.chart.getDatasetMeta(datasetIndex).data[index][this.id]; + }, + /** * Used to get the value to display in the tooltip for the data at the given index * @param index diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index b9e51bcb162..09cd68cce9d 100644 --- a/src/scales/scale.category.js +++ b/src/scales/scale.category.js @@ -7,19 +7,15 @@ var defaultConfig = { }; module.exports = Scale.extend({ - /** - * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those - * else fall back to data.labels - * @private - */ - getLabels: function() { - var data = this.chart.data; - return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; + parse: function(raw) { + var labels = this._getLabels(); + var index = labels.indexOf(raw); + return index === -1 ? null : index; }, determineDataLimits: function() { var me = this; - var labels = me.getLabels(); + var labels = me._getLabels(); me.minIndex = 0; me.maxIndex = labels.length - 1; var findIndex; @@ -42,20 +38,15 @@ module.exports = Scale.extend({ buildTicks: function() { var me = this; - var labels = me.getLabels(); + var labels = me._getLabels(); // If we are viewing some subset of labels, slice the original array me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); }, getLabelForIndex: function(index, datasetIndex) { var me = this; - var chart = me.chart; - if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { - return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); - } - - return me.ticks[index - me.minIndex]; + return me.ticks[me._getRawValue(index, datasetIndex) - me.minIndex]; }, // Used to get data value locations. Value can either be an index or a numerical value @@ -72,7 +63,7 @@ module.exports = Scale.extend({ valueCategory = me.isHorizontal() ? value.x : value.y; } if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { - var labels = me.getLabels(); + var labels = me._getLabels(); value = valueCategory || value; var idx = labels.indexOf(value); index = idx !== -1 ? idx : index; diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index 7f68279db6c..5aaf7bb23af 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -85,6 +85,10 @@ function generateTicks(generationOptions, dataRange) { } module.exports = Scale.extend({ + parse: function(raw) { + return +raw; + }, + getRightValue: function(value) { if (typeof value === 'string') { return +value; diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index fd67f0b19a4..135ea714eb2 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -68,6 +68,10 @@ function nonNegativeOrDefault(value, defaultValue) { } module.exports = Scale.extend({ + parse: function(raw) { + return +raw; + }, + determineDataLimits: function() { var me = this; var opts = me.options; diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 0db15bb7859..3819dddeb79 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -466,6 +466,10 @@ module.exports = Scale.extend({ Scale.prototype.initialize.call(this); }, + parse: function(raw) { + return toTimestamp(raw, this.options); + }, + update: function() { var me = this; var options = me.options; From 5b0aeff9e9b8aabc66030ccaf44310107bf00625 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 09:33:41 +0200 Subject: [PATCH 02/16] _parse split into _parseArrayData, _parseObjectData, _parsePlainData --- src/core/core.datasetController.js | 90 +++++++++++++++++++----------- src/core/core.scale.js | 17 +++++- src/scales/scale.category.js | 4 +- src/scales/scale.linearbase.js | 2 +- src/scales/scale.logarithmic.js | 2 +- src/scales/scale.time.js | 9 ++- 6 files changed, 83 insertions(+), 41 deletions(-) diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index b67fa9637dc..dde8f17d27f 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -321,52 +321,74 @@ helpers.extend(DatasetController.prototype, { /** * @private - * parse data to internal representation utilizing scales */ _parse: function(start, count) { + var me = this; + var data = me.getDataset().data; + + if (data && data.length) { + if (helpers.isArray(data[0])) { + me._parseArrayData(start, count); + } else if (helpers.isObject(data[0])) { + me._parseObjectData(start, count); + } else { + me._parsePlainData(start, count); + } + } + }, + + /** + * @private + */ + _parseArrayData: function(start, count) { var me = this; var meta = me.getMeta(); var data = me.getDataset().data; var xID = meta.xAxisID; var yID = meta.yAxisID; - var indexScale = me._getIndexScale(); - var valueScale = me._getValueScale(); var xScale = me.getScaleForId(xID); var yScale = me.getScaleForId(yID); - var labels = indexScale._getLabels() || []; - var i, ilen, j, jlen, v, key, keys, metaData; - + var i, ilen, v, metaData; for (i = start, ilen = start + count; i < ilen; ++i) { metaData = meta.data[i]; v = data[i]; - if (helpers.isArray(v) && v.length === 2) { - metaData[xID] = xScale.parse(v[0]); - metaData[yID] = yScale.parse(v[1]); - } else if (helpers.isObject(v)) { - keys = Object.keys(v); - metaData[xID] = null; - metaData[yID] = null; - for (j = 0, jlen = keys.length; j < jlen; ++j) { - key = keys[j]; - if (key === 'x') { - metaData[xID] = xScale.parse(v.x); - } else if (key === 'y') { - metaData[yID] = yScale.parse(v.y); - } - } - if (v.t) { - if (metaData[xID] === null) { - metaData[xID] = xScale.parse(v.t); - } - if (metaData[yID] === null) { - metaData[yID] = xScale.parse(v.t); - } - } - } else { - metaData[valueScale.id] = valueScale.parse(v); - if (indexScale !== valueScale && i < labels.length) { - metaData[indexScale.id] = indexScale.parse(labels[i]); - } + metaData[xID] = xScale._parse(v[0]); + metaData[yID] = yScale._parse(v[1]); + } + }, + + /** + * @private + */ + _parseObjectData: function(start, count) { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var i, ilen; + for (i = start, ilen = start + count; i < ilen; ++i) { + meta.data[i][meta.xAxisID] = xScale._parseObject(data[i], 'x'); + meta.data[i][meta.yAxisID] = yScale._parseObject(data[i], 'y'); + } + }, + + /** + * @private + */ + _parsePlainData: function(start, count) { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var indexScale = me._getIndexScale(); + var valueScale = me._getValueScale(); + var labels = indexScale._getLabels() || []; + var i, ilen; + + for (i = start, ilen = start + count; i < ilen; ++i) { + meta.data[i][valueScale.id] = valueScale._parse(data[i]); + if (indexScale !== valueScale && i < labels.length) { + meta.data[i][indexScale.id] = indexScale._parse(labels[i]); } } }, diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 483b7ab0bac..624d502f786 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -105,10 +105,23 @@ module.exports = Element.extend({ * @param {*} raw * @since 2.9 */ - parse: function(raw) { + _parse: function(raw) { return raw; }, + /** + * Function that parses a object for axis to internal representation. + * @param {object} obj + * @param {string} axis + * @since 2.9 + */ + _parseObject: function(obj, axis) { + if (obj.hasOwnProperty(axis)) { + return this._parse(obj[axis]); + } + return null; + }, + /** * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those * else fall back to data.labels @@ -557,7 +570,7 @@ module.exports = Element.extend({ return rawValue; }, - _getRawValue: function(index, datasetIndex) { + _getParsedValue: function(index, datasetIndex) { return this.chart.getDatasetMeta(datasetIndex).data[index][this.id]; }, diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index 09cd68cce9d..bba8b8857b0 100644 --- a/src/scales/scale.category.js +++ b/src/scales/scale.category.js @@ -7,7 +7,7 @@ var defaultConfig = { }; module.exports = Scale.extend({ - parse: function(raw) { + _parse: function(raw) { var labels = this._getLabels(); var index = labels.indexOf(raw); return index === -1 ? null : index; @@ -46,7 +46,7 @@ module.exports = Scale.extend({ getLabelForIndex: function(index, datasetIndex) { var me = this; - return me.ticks[me._getRawValue(index, datasetIndex) - me.minIndex]; + return me.ticks[me._getParsedValue(index, datasetIndex) - me.minIndex]; }, // Used to get data value locations. Value can either be an index or a numerical value diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index 5aaf7bb23af..c4d7ceec563 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -85,7 +85,7 @@ function generateTicks(generationOptions, dataRange) { } module.exports = Scale.extend({ - parse: function(raw) { + _parse: function(raw) { return +raw; }, diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 135ea714eb2..89fc1310461 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -68,7 +68,7 @@ function nonNegativeOrDefault(value, defaultValue) { } module.exports = Scale.extend({ - parse: function(raw) { + _parse: function(raw) { return +raw; }, diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 3819dddeb79..dcd45879db3 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -466,10 +466,17 @@ module.exports = Scale.extend({ Scale.prototype.initialize.call(this); }, - parse: function(raw) { + _parse: function(raw) { return toTimestamp(raw, this.options); }, + _parseObject: function(obj, axis) { + if (obj.t) { + return this._parse(obj.t); + } + return Scale.prototype._parseObject.call(obj, axis); + }, + update: function() { var me = this; var options = me.options; From 62d2207ae8e9390da1678acaf0c03fd7a33f1cc5 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 12:30:43 +0200 Subject: [PATCH 03/16] update contorller.bubble to use parsed data --- src/controllers/controller.bubble.js | 12 +++++++++--- src/core/core.datasetController.js | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index d40b4393b51..bdf6132e829 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -42,6 +42,13 @@ defaults._set('bubble', { }); module.exports = DatasetController.extend({ + /** + * @private + */ + _parseCustomObjectData: function(obj, metaData) { + metaData.r = +obj.r; + }, + /** * @protected */ @@ -71,11 +78,10 @@ module.exports = DatasetController.extend({ var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var options = me._resolveElementOptions(point, index); - var data = me.getDataset().data[index]; var dsIndex = me.index; - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); - var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(point[xScale.id], index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(point[yScale.id], index, dsIndex); point._xScale = xScale; point._yScale = yScale; diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index dde8f17d27f..cb286a7ed7b 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -357,6 +357,8 @@ helpers.extend(DatasetController.prototype, { } }, + _parseCustomObjectData: helpers.noop, + /** * @private */ @@ -370,6 +372,7 @@ helpers.extend(DatasetController.prototype, { for (i = start, ilen = start + count; i < ilen; ++i) { meta.data[i][meta.xAxisID] = xScale._parseObject(data[i], 'x'); meta.data[i][meta.yAxisID] = yScale._parseObject(data[i], 'y'); + me._parseCustomObjectData(data[i], meta.data[i]); } }, From f9c51a76b579f8fd45912fb83b6b9971dc654472 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 12:50:37 +0200 Subject: [PATCH 04/16] custom _parse + usage to doughnut --- src/controllers/controller.doughnut.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index c207356c39e..fa700567124 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -127,7 +127,15 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, - _parse: helpers.noop, + _parse: function(start, count) { + var me = this; + var data = me.getDataset().data; + var metaData = me.getMeta().data; + var i, ilen; + for (i = start, ilen = start + count; i < ilen; ++i) { + metaData[i]._val = +data[i]; + } + }, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { @@ -209,7 +217,7 @@ module.exports = DatasetController.extend({ var startAngle = opts.rotation; // non reset case handled later var endAngle = opts.rotation; // non reset case handled later var dataset = me.getDataset(); - var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(arc._val) * (opts.circumference / (2.0 * Math.PI)); var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; var options = arc._options || {}; @@ -253,14 +261,13 @@ module.exports = DatasetController.extend({ }, calculateTotal: function() { - var dataset = this.getDataset(); - var meta = this.getMeta(); + var metaData = this.getMeta().data; var total = 0; var value; - helpers.each(meta.data, function(element, index) { - value = dataset.data[index]; - if (!isNaN(value) && !element.hidden) { + helpers.each(metaData, function(arc) { + value = arc ? arc._val : NaN; + if (!isNaN(value) && !arc.hidden) { total += Math.abs(value); } }); From cb6c0519af07e78c7788b5549d97aaeda8a88f9a Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 12:58:14 +0200 Subject: [PATCH 05/16] use parsed data for x-axis --- src/controllers/controller.line.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 49665e2299a..71f145fa065 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -89,6 +89,7 @@ module.exports = DatasetController.extend({ var dataset = me.getDataset(); var datasetIndex = me.index; var value = dataset.data[index]; + var parsedData = meta.data[index]; var yScale = me.getScaleForId(meta.yAxisID); var xScale = me.getScaleForId(meta.xAxisID); var lineModel = meta.dataset._model; @@ -96,7 +97,7 @@ module.exports = DatasetController.extend({ var options = me._resolvePointOptions(point, index); - x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); + x = xScale.getPixelForValue(parsedData[xScale.id], index, datasetIndex); y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); // Utility From 40db5ed01d93deda681fae7c7272c9768a540e95 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 13:44:24 +0200 Subject: [PATCH 06/16] undefined instead of hasOwnProperty --- src/core/core.scale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 624d502f786..9dec4a4ac14 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -116,7 +116,7 @@ module.exports = Element.extend({ * @since 2.9 */ _parseObject: function(obj, axis) { - if (obj.hasOwnProperty(axis)) { + if (obj[axis] !== undefined) { return this._parse(obj[axis]); } return null; From 3fa6a71bf25479211c83d5c5e93ff296473630e3 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 15:00:00 +0200 Subject: [PATCH 07/16] rest of controller.bar to use parsed data --- src/controllers/controller.bar.js | 14 ++++---------- src/core/core.datasetController.js | 12 ++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 992333a25c2..e1c2379b383 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -284,8 +284,7 @@ module.exports = DatasetController.extend({ var meta = me.getMeta(); var scale = me._getValueScale(); var isHorizontal = scale.isHorizontal(); - var datasets = chart.data.datasets; - var value = +scale.getRightValue(datasets[datasetIndex].data[index]); + var value = me._getParsedValue(index, scale); var minBarLength = scale.options.minBarLength; var stacked = scale.options.stacked; var stack = meta.stack; @@ -296,12 +295,8 @@ module.exports = DatasetController.extend({ for (i = 0; i < datasetIndex; ++i) { imeta = chart.getDatasetMeta(i); - if (imeta.bar && - imeta.stack === stack && - imeta.controller._getValueScaleId() === scale.id && - chart.isDatasetVisible(i)) { - - ivalue = +scale.getRightValue(datasets[i].data[index]); + if (imeta.bar && imeta.stack === stack && chart.isDatasetVisible(i)) { + ivalue = imeta.controller._getParsedValue(index, scale); if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { start += ivalue; } @@ -359,14 +354,13 @@ module.exports = DatasetController.extend({ var chart = me.chart; var scale = me._getValueScale(); var rects = me.getMeta().data; - var dataset = me.getDataset(); var ilen = rects.length; var i = 0; helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { - if (!isNaN(scale.getRightValue(dataset.data[i]))) { + if (!isNaN(me._getParsedValue(i, scale))) { rects[i].draw(); } } diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index cb286a7ed7b..4e232e3b49d 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -304,6 +304,7 @@ helpers.extend(DatasetController.prototype, { if (numData < numMeta) { meta.data.splice(numData, numMeta - numData); + me._parse(0, numData); } else if (numData > numMeta) { me.insertElements(numMeta, numData - numMeta); } @@ -396,6 +397,17 @@ helpers.extend(DatasetController.prototype, { } }, + /** + * @private + */ + _getParsedValue: function(index, scale) { + var data = this.getMeta().data; + if (index < 0 || index >= data.length) { + return NaN; + } + return data[index][scale.id]; + }, + /** * @private */ From 464865a9e9a82a2dbc5c7ee10cd5c7ae0537535e Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 16:32:13 +0200 Subject: [PATCH 08/16] use parsed values in most scales --- src/controllers/controller.line.js | 23 +++++++++-------------- src/core/core.datasetController.js | 2 ++ src/scales/scale.linear.js | 11 +++++------ src/scales/scale.linearbase.js | 7 +++++++ src/scales/scale.logarithmic.js | 14 ++++++++++---- src/scales/scale.radialLinear.js | 6 +++--- src/scales/scale.time.js | 2 +- test/specs/scale.logarithmic.tests.js | 2 +- 8 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 71f145fa065..65b530237ca 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -86,10 +86,7 @@ module.exports = DatasetController.extend({ var me = this; var meta = me.getMeta(); var custom = point.custom || {}; - var dataset = me.getDataset(); var datasetIndex = me.index; - var value = dataset.data[index]; - var parsedData = meta.data[index]; var yScale = me.getScaleForId(meta.yAxisID); var xScale = me.getScaleForId(meta.xAxisID); var lineModel = meta.dataset._model; @@ -97,8 +94,8 @@ module.exports = DatasetController.extend({ var options = me._resolvePointOptions(point, index); - x = xScale.getPixelForValue(parsedData[xScale.id], index, datasetIndex); - y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); + x = xScale.getPixelForValue(me._getParsedValue(index, xScale), index, datasetIndex); + y = reset ? yScale.getBasePixel() : me.calculatePointY(me._getParsedValue(index, yScale), index, datasetIndex); // Utility point._xScale = xScale; @@ -225,27 +222,25 @@ module.exports = DatasetController.extend({ var yScale = me.getScaleForId(meta.yAxisID); var sumPos = 0; var sumNeg = 0; - var i, ds, dsMeta; + var i, dsMeta; if (yScale.options.stacked) { for (i = 0; i < datasetIndex; i++) { - ds = chart.data.datasets[i]; dsMeta = chart.getDatasetMeta(i); - if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { - var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); + if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) { + var stackedRightValue = dsMeta.controller._getParsedValue(index, yScale); if (stackedRightValue < 0) { sumNeg += stackedRightValue || 0; - } else { + } else if (stackedRightValue > 0) { sumPos += stackedRightValue || 0; } } } - var rightValue = Number(yScale.getRightValue(value)); - if (rightValue < 0) { - return yScale.getPixelForValue(sumNeg + rightValue); + if (value < 0) { + return yScale.getPixelForValue(sumNeg + value); } - return yScale.getPixelForValue(sumPos + rightValue); + return yScale.getPixelForValue(sumPos + value); } return yScale.getPixelForValue(value); diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 4e232e3b49d..81cd0cf6e89 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -307,6 +307,8 @@ helpers.extend(DatasetController.prototype, { me._parse(0, numData); } else if (numData > numMeta) { me.insertElements(numMeta, numData - numMeta); + } else { + me._parse(0, numData); } }, diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js index a2199b66e78..9a4df445a93 100644 --- a/src/scales/scale.linear.js +++ b/src/scales/scale.linear.js @@ -70,7 +70,7 @@ module.exports = LinearScaleBase.extend({ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); + var value = meta.controller._getParsedValue(index, me); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -101,8 +101,8 @@ module.exports = LinearScaleBase.extend({ 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 = +me.getRightValue(rawValue); + helpers.each(meta.data, function(metaData, index) { + var value = meta.controller._getParsedValue(index, me); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -151,17 +151,16 @@ module.exports = LinearScaleBase.extend({ }, getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); }, // Utils - getPixelForValue: function(value) { + getPixelForValue: function(rightValue) { // This must be called after fit has been run so that // this.left, this.top, this.right, and this.bottom have been defined var me = this; var start = me.start; - var rightValue = +me.getRightValue(value); var pixel; var range = me.end - start; diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index c4d7ceec563..b9d694e10d8 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -86,6 +86,13 @@ function generateTicks(generationOptions, dataRange) { module.exports = Scale.extend({ _parse: function(raw) { + if (helpers.isNullOrUndef(raw)) { + return NaN; + } + if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(raw)) { + return NaN; + } + return +raw; }, diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 89fc1310461..57ae9325dba 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -69,6 +69,13 @@ function nonNegativeOrDefault(value, defaultValue) { module.exports = Scale.extend({ _parse: function(raw) { + if (helpers.isNullOrUndef(raw)) { + return NaN; + } + if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(raw)) { + return NaN; + } + return +raw; }, @@ -122,7 +129,7 @@ module.exports = Scale.extend({ helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerStack[key]; - var value = +me.getRightValue(rawValue); + var value = meta.controller._getParsedValue(index, me); // invalid, hidden and negative values are ignored if (isNaN(value) || meta.data[index].hidden || value < 0) { return; @@ -147,7 +154,7 @@ module.exports = Scale.extend({ var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); + var value = meta.controller._getParsedValue(index, me); // invalid, hidden and negative values are ignored if (isNaN(value) || meta.data[index].hidden || value < 0) { return; @@ -251,7 +258,7 @@ module.exports = Scale.extend({ // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); }, getPixelForTick: function(index) { @@ -280,7 +287,6 @@ module.exports = Scale.extend({ var offset = 0; var innerDimension, pixel, start, end, sign; - value = +me.getRightValue(value); if (reverse) { start = me.end; end = me.start; diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index ce605bde404..ca3fd5ea2bc 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -341,8 +341,8 @@ module.exports = LinearScaleBase.extend({ if (chart.isDatasetVisible(datasetIndex)) { var meta = chart.getDatasetMeta(datasetIndex); - helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); + helpers.each(meta.data, function(metaData, index) { + var value = meta.controller._getParsedValue(index, me); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -375,7 +375,7 @@ module.exports = LinearScaleBase.extend({ }, getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); }, fit: function() { diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index dcd45879db3..ab9930771c0 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -471,7 +471,7 @@ module.exports = Scale.extend({ }, _parseObject: function(obj, axis) { - if (obj.t) { + if (obj && obj.t) { return this._parse(obj.t); } return Scale.prototype._parseObject.call(obj, axis); diff --git a/test/specs/scale.logarithmic.tests.js b/test/specs/scale.logarithmic.tests.js index dd7c7cce94a..fc7e67f9f90 100644 --- a/test/specs/scale.logarithmic.tests.js +++ b/test/specs/scale.logarithmic.tests.js @@ -749,7 +749,7 @@ describe('Logarithmic Scale tests', function() { } }); - expect(chart.scales.yScale1.getLabelForIndex(0, 2)).toBe(150); + expect(chart.scales.yScale0.getLabelForIndex(0, 2)).toBe(150); }); describe('when', function() { From 47efe4a8007293b3dc179121339455f4f179def8 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 17:55:46 +0200 Subject: [PATCH 09/16] getPixelForParsedValue --- src/core/core.scale.js | 10 ++++++++- src/scales/scale.category.js | 40 +++++++++++++++++++++--------------- src/scales/scale.time.js | 6 +++--- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 9dec4a4ac14..91e4f6de480 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -570,10 +570,18 @@ module.exports = Element.extend({ return rawValue; }, + /** + * @private + */ _getParsedValue: function(index, datasetIndex) { return this.chart.getDatasetMeta(datasetIndex).data[index][this.id]; }, + /** + * @private + */ + _getPixelForParsedValue: helpers.noop, + /** * Used to get the value to display in the tooltip for the data at the given index * @param index @@ -582,7 +590,7 @@ module.exports = Element.extend({ getLabelForIndex: helpers.noop, /** - * Returns the location of the given data point. Value can either be an index or a numerical value + * Returns the location of the given data point. Value can be whatever * The coordinate (0, 0) is at the upper-left corner of the canvas * @param value * @param index diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index bba8b8857b0..20104afafc3 100644 --- a/src/scales/scale.category.js +++ b/src/scales/scale.category.js @@ -49,29 +49,15 @@ module.exports = Scale.extend({ return me.ticks[me._getParsedValue(index, datasetIndex) - me.minIndex]; }, - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: function(value, index) { + _getPixelForParsedValue: function(value) { var me = this; var offset = me.options.offset; // 1 is added because we need the length but we have the indexes var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); - // If value is a data object, then index is the index in the data array, - // not the index of the scale. We need to change that. - var valueCategory; - if (value !== undefined && value !== null) { - valueCategory = me.isHorizontal() ? value.x : value.y; - } - if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { - var labels = me._getLabels(); - value = valueCategory || value; - var idx = labels.indexOf(value); - index = idx !== -1 ? idx : index; - } - if (me.isHorizontal()) { var valueWidth = me.width / offsetAmt; - var widthOffset = (valueWidth * (index - me.minIndex)); + var widthOffset = (valueWidth * (value - me.minIndex)); if (offset) { widthOffset += (valueWidth / 2); @@ -80,7 +66,7 @@ module.exports = Scale.extend({ return me.left + widthOffset; } var valueHeight = me.height / offsetAmt; - var heightOffset = (valueHeight * (index - me.minIndex)); + var heightOffset = (valueHeight * (value - me.minIndex)); if (offset) { heightOffset += (valueHeight / 2); @@ -89,6 +75,26 @@ module.exports = Scale.extend({ return me.top + heightOffset; }, + // Used to get data value locations. Value can either be an index or a numerical value + getPixelForValue: function(value, index) { + var me = this; + + // If value is a data object, then index is the index in the data array, + // not the index of the scale. We need to change that. + var valueCategory; + if (value !== undefined && value !== null) { + valueCategory = me.isHorizontal() ? value.x : value.y; + } + if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { + var labels = me._getLabels(); + value = valueCategory || value; + var idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + } + + return me._getPixelForParsedValue(index); + }, + getPixelForTick: function(index) { return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); }, diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index ab9930771c0..679419c026b 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -696,7 +696,7 @@ module.exports = Scale.extend({ /** * @private */ - getPixelForOffset: function(time) { + _getPixelForParsedValue: function(time) { var me = this; var isReverse = me.options.ticks.reverse; var size = me._horizontal ? me.width : me.height; @@ -720,14 +720,14 @@ module.exports = Scale.extend({ } if (time !== null) { - return me.getPixelForOffset(time); + return me._getPixelForParsedValue(time); } }, getPixelForTick: function(index) { var ticks = this.getTicks(); return index >= 0 && index < ticks.length ? - this.getPixelForOffset(ticks[index].value) : + this._getPixelForParsedValue(ticks[index].value) : null; }, From 435bba5f64018b82d960205c36e015878fbd9f20 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 31 Jan 2019 18:13:07 +0200 Subject: [PATCH 10/16] _getPixel --- src/controllers/controller.bar.js | 4 ++-- src/controllers/controller.bubble.js | 4 ++-- src/controllers/controller.line.js | 6 +++--- src/core/core.datasetController.js | 4 ++-- src/core/core.scale.js | 5 +++-- src/scales/scale.category.js | 11 +++++++++-- src/scales/scale.linear.js | 17 ++++++++++------- src/scales/scale.logarithmic.js | 12 ++++++++---- src/scales/scale.radialLinear.js | 4 ++-- src/scales/scale.time.js | 6 +++--- 10 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index e1c2379b383..1195b46aa6f 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -304,8 +304,8 @@ module.exports = DatasetController.extend({ } } - base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + value); + base = scale._getPixel(start); + head = scale._getPixel(start + value); size = head - base; if (minBarLength !== undefined && Math.abs(size) < minBarLength) { diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index bdf6132e829..8e1f1917833 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -80,8 +80,8 @@ module.exports = DatasetController.extend({ var options = me._resolveElementOptions(point, index); var dsIndex = me.index; - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(point[xScale.id], index, dsIndex); - var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(point[yScale.id], index, dsIndex); + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale._getPixel(point[xScale.id]); + var y = reset ? yScale.getBasePixel() : yScale._getPixel(point[yScale.id]); point._xScale = xScale; point._yScale = yScale; diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 65b530237ca..5e31219c05e 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -238,12 +238,12 @@ module.exports = DatasetController.extend({ } if (value < 0) { - return yScale.getPixelForValue(sumNeg + value); + return yScale._getPixel(sumNeg + value); } - return yScale.getPixelForValue(sumPos + value); + return yScale._getPixel(sumPos + value); } - return yScale.getPixelForValue(value); + return yScale._getPixel(value); }, updateBezierControlPoints: function() { diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 81cd0cf6e89..e05f1e40022 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -373,8 +373,8 @@ helpers.extend(DatasetController.prototype, { var yScale = me.getScaleForId(meta.yAxisID); var i, ilen; for (i = start, ilen = start + count; i < ilen; ++i) { - meta.data[i][meta.xAxisID] = xScale._parseObject(data[i], 'x'); - meta.data[i][meta.yAxisID] = yScale._parseObject(data[i], 'y'); + meta.data[i][meta.xAxisID] = xScale._parseObject(data[i], 'x', i); + meta.data[i][meta.yAxisID] = yScale._parseObject(data[i], 'y', i); me._parseCustomObjectData(data[i], meta.data[i]); } }, diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 91e4f6de480..4d30ecb38fe 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -113,6 +113,7 @@ module.exports = Element.extend({ * Function that parses a object for axis to internal representation. * @param {object} obj * @param {string} axis + * @param {number} index * @since 2.9 */ _parseObject: function(obj, axis) { @@ -574,13 +575,13 @@ module.exports = Element.extend({ * @private */ _getParsedValue: function(index, datasetIndex) { - return this.chart.getDatasetMeta(datasetIndex).data[index][this.id]; + return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); }, /** * @private */ - _getPixelForParsedValue: helpers.noop, + _getPixel: helpers.noop, /** * Used to get the value to display in the tooltip for the data at the given index diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index 20104afafc3..930f3fcf1c6 100644 --- a/src/scales/scale.category.js +++ b/src/scales/scale.category.js @@ -13,6 +13,13 @@ module.exports = Scale.extend({ return index === -1 ? null : index; }, + _parseObject: function(obj, axis, index) { + if (obj[axis] !== undefined) { + return this._parse(obj[axis]) || index; + } + return null; + }, + determineDataLimits: function() { var me = this; var labels = me._getLabels(); @@ -49,7 +56,7 @@ module.exports = Scale.extend({ return me.ticks[me._getParsedValue(index, datasetIndex) - me.minIndex]; }, - _getPixelForParsedValue: function(value) { + _getPixel: function(value) { var me = this; var offset = me.options.offset; // 1 is added because we need the length but we have the indexes @@ -92,7 +99,7 @@ module.exports = Scale.extend({ index = idx !== -1 ? idx : index; } - return me._getPixelForParsedValue(index); + return me._getPixel(index); }, getPixelForTick: function(index) { diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js index 9a4df445a93..10ae36c3be2 100644 --- a/src/scales/scale.linear.js +++ b/src/scales/scale.linear.js @@ -70,7 +70,7 @@ module.exports = LinearScaleBase.extend({ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = meta.controller._getParsedValue(index, me); + var value = me._getParsedValue(index, datasetIndex); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -102,7 +102,7 @@ module.exports = LinearScaleBase.extend({ var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(meta.data, function(metaData, index) { - var value = meta.controller._getParsedValue(index, me); + var value = me._getParsedValue(index, datasetIndex); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -151,11 +151,10 @@ module.exports = LinearScaleBase.extend({ }, getLabelForIndex: function(index, datasetIndex) { - return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); + return this._getParsedValue(index, datasetIndex); }, - // Utils - getPixelForValue: function(rightValue) { + _getPixel: function(value) { // This must be called after fit has been run so that // this.left, this.top, this.right, and this.bottom have been defined var me = this; @@ -165,13 +164,17 @@ module.exports = LinearScaleBase.extend({ var range = me.end - start; if (me.isHorizontal()) { - pixel = me.left + (me.width / range * (rightValue - start)); + pixel = me.left + (me.width / range * (value - start)); } else { - pixel = me.bottom - (me.height / range * (rightValue - start)); + pixel = me.bottom - (me.height / range * (value - start)); } return pixel; }, + getPixelForValue: function(value) { + return this._getPixel(value); + }, + getValueForPixel: function(pixel) { var me = this; var isHorizontal = me.isHorizontal(); diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 57ae9325dba..4ab50718dc1 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -129,7 +129,7 @@ module.exports = Scale.extend({ helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerStack[key]; - var value = meta.controller._getParsedValue(index, me); + var value = me._getParsedValue(index, datasetIndex); // invalid, hidden and negative values are ignored if (isNaN(value) || meta.data[index].hidden || value < 0) { return; @@ -154,7 +154,7 @@ module.exports = Scale.extend({ var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = meta.controller._getParsedValue(index, me); + var value = me._getParsedValue(index, datasetIndex); // invalid, hidden and negative values are ignored if (isNaN(value) || meta.data[index].hidden || value < 0) { return; @@ -258,7 +258,7 @@ module.exports = Scale.extend({ // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { - return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); + return this._getParsedValue(index, datasetIndex); }, getPixelForTick: function(index) { @@ -278,7 +278,7 @@ module.exports = Scale.extend({ return significand * Math.pow(10, exp); }, - getPixelForValue: function(value) { + _getPixel: function(value) { var me = this; var tickOpts = me.options.ticks; var reverse = tickOpts.reverse; @@ -318,6 +318,10 @@ module.exports = Scale.extend({ return pixel; }, + getPixelForValue: function(value) { + return this._getPixel(value); + }, + getValueForPixel: function(pixel) { var me = this; var tickOpts = me.options.ticks; diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index ca3fd5ea2bc..be63e7747ca 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -342,7 +342,7 @@ module.exports = LinearScaleBase.extend({ var meta = chart.getDatasetMeta(datasetIndex); helpers.each(meta.data, function(metaData, index) { - var value = meta.controller._getParsedValue(index, me); + var value = me._getParsedValue(index, datasetIndex); if (isNaN(value) || meta.data[index].hidden) { return; } @@ -375,7 +375,7 @@ module.exports = LinearScaleBase.extend({ }, getLabelForIndex: function(index, datasetIndex) { - return this.chart.getDatasetMeta(datasetIndex).controller._getParsedValue(index, this); + return this._getParsedValue(index, datasetIndex); }, fit: function() { diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 679419c026b..2531ec4e2bb 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -696,7 +696,7 @@ module.exports = Scale.extend({ /** * @private */ - _getPixelForParsedValue: function(time) { + _getPixel: function(time) { var me = this; var isReverse = me.options.ticks.reverse; var size = me._horizontal ? me.width : me.height; @@ -720,14 +720,14 @@ module.exports = Scale.extend({ } if (time !== null) { - return me._getPixelForParsedValue(time); + return me._getPixel(time); } }, getPixelForTick: function(index) { var ticks = this.getTicks(); return index >= 0 && index < ticks.length ? - this._getPixelForParsedValue(ticks[index].value) : + this._getPixel(ticks[index].value) : null; }, From 2eaa78693833a57396110100d1f92d9217c5b2c4 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sat, 2 Mar 2019 12:57:10 +0200 Subject: [PATCH 11/16] rebase - adapter options --- src/scales/scale.time.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index 2531ec4e2bb..e43b12cf336 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -462,12 +462,17 @@ var defaultConfig = { module.exports = Scale.extend({ initialize: function() { - this.mergeTicksOptions(); - Scale.prototype.initialize.call(this); + var me = this; + me.mergeTicksOptions(); + + // construct adapter for early parsing + me._adapter = new adapters._date(me.options.adapters.date); + + Scale.prototype.initialize.call(me); }, _parse: function(raw) { - return toTimestamp(raw, this.options); + return toTimestamp(this, raw); }, _parseObject: function(obj, axis) { From b6ff353abdcba2add0fde1f17735f4b227b1f3e0 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sat, 2 Mar 2019 15:25:17 +0200 Subject: [PATCH 12/16] stacking by value --- src/controllers/controller.bar.js | 15 +++--- src/controllers/controller.bubble.js | 4 +- src/core/core.datasetController.js | 69 +++++++++++++++++++++++----- src/core/core.scale.js | 9 ++++ 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 1195b46aa6f..d4cbc45f21d 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -201,15 +201,16 @@ module.exports = DatasetController.extend({ var stacked = scale.options.stacked; var ilen = last === undefined ? chart.data.datasets.length : last + 1; var stacks = []; - var i, meta; + var i, meta, stack; for (i = 0; i < ilen; ++i) { meta = chart.getDatasetMeta(i); + stack = meta.stack; if (meta.bar && chart.isDatasetVisible(i) && (stacked === false || - (stacked === true && stacks.indexOf(meta.stack) === -1) || - (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { - stacks.push(meta.stack); + (stacked === true && stacks.indexOf(stack) === -1) || + (stacked === undefined && (stack === undefined || stacks.indexOf(stack) === -1)))) { + stacks.push(stack); } } @@ -282,8 +283,10 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var meta = me.getMeta(); + var indexValue = me._getParsedValue(index, me._getIndexScale()); var scale = me._getValueScale(); var isHorizontal = scale.isHorizontal(); + var axis = scale._getAxis(); var value = me._getParsedValue(index, scale); var minBarLength = scale.options.minBarLength; var stacked = scale.options.stacked; @@ -296,8 +299,8 @@ module.exports = DatasetController.extend({ imeta = chart.getDatasetMeta(i); if (imeta.bar && imeta.stack === stack && chart.isDatasetVisible(i)) { - ivalue = imeta.controller._getParsedValue(index, scale); - if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + ivalue = imeta.controller._getParsedValueByValue(axis, indexValue); + if (ivalue !== undefined && ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0))) { start += ivalue; } } diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index 8e1f1917833..b7f32403ac1 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -80,8 +80,8 @@ module.exports = DatasetController.extend({ var options = me._resolveElementOptions(point, index); var dsIndex = me.index; - var x = reset ? xScale.getPixelForDecimal(0.5) : xScale._getPixel(point[xScale.id]); - var y = reset ? yScale.getBasePixel() : yScale._getPixel(point[yScale.id]); + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale._getPixel(point._parsed[xScale.id]); + var y = reset ? yScale.getBasePixel() : yScale._getPixel(point._parsed[yScale.id]); point._xScale = xScale; point._yScale = yScale; diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index e05f1e40022..8589133d50f 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -187,7 +187,8 @@ helpers.extend(DatasetController.prototype, { return type && new type({ _chart: me.chart, _datasetIndex: me.index, - _index: index + _index: index, + _parsed: {} }); }, @@ -345,18 +346,26 @@ helpers.extend(DatasetController.prototype, { */ _parseArrayData: function(start, count) { var me = this; + var datasetIndex = me.index; var meta = me.getMeta(); + var xref = meta._xref || (meta._xref = {x: {}, y: {}}); var data = me.getDataset().data; var xID = meta.xAxisID; var yID = meta.yAxisID; var xScale = me.getScaleForId(xID); var yScale = me.getScaleForId(yID); - var i, ilen, v, metaData; + var i, ilen, v, parsed, x, y, xr, yr; for (i = start, ilen = start + count; i < ilen; ++i) { - metaData = meta.data[i]; + parsed = meta.data[i]._parsed; v = data[i]; - metaData[xID] = xScale._parse(v[0]); - metaData[yID] = yScale._parse(v[1]); + parsed[xID] = x = xScale._parse(v[0]); + parsed[yID] = y = yScale._parse(v[1]); + + // store cross reference values, for stacking + xr = xref.x[x] || (xref.x[x] = []); + yr = xref.y[y] || (xref.y[y] = []); + xr[datasetIndex] = y; + yr[datasetIndex] = x; } }, @@ -367,15 +376,27 @@ helpers.extend(DatasetController.prototype, { */ _parseObjectData: function(start, count) { var me = this; + var datasetIndex = me.index; var meta = me.getMeta(); + var xref = meta._xref || (meta._xref = {x: {}, y: {}}); var data = me.getDataset().data; + var xID = meta.xAxisID; + var yID = meta.yAxisID; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); - var i, ilen; + var i, ilen, v, parsed, x, y, xr, yr; for (i = start, ilen = start + count; i < ilen; ++i) { - meta.data[i][meta.xAxisID] = xScale._parseObject(data[i], 'x', i); - meta.data[i][meta.yAxisID] = yScale._parseObject(data[i], 'y', i); + parsed = meta.data[i]._parsed; + v = data[i]; + parsed[xID] = x = xScale._parseObject(v, 'x', i); + parsed[yID] = y = yScale._parseObject(data[i], 'y', i); me._parseCustomObjectData(data[i], meta.data[i]); + + // store cross reference values, for stacking + xr = xref.x[x] || (xref.x[x] = []); + yr = xref.y[y] || (xref.y[y] = []); + xr[datasetIndex] = y; + yr[datasetIndex] = x; } }, @@ -384,17 +405,31 @@ helpers.extend(DatasetController.prototype, { */ _parsePlainData: function(start, count) { var me = this; + var datasetIndex = me.index; var meta = me.getMeta(); + var xref = meta._xref || (meta._xref = {x: {}, y: {}}); var data = me.getDataset().data; var indexScale = me._getIndexScale(); var valueScale = me._getValueScale(); var labels = indexScale._getLabels() || []; - var i, ilen; + var i, ilen, parsed, x, y, xr, yr; for (i = start, ilen = start + count; i < ilen; ++i) { - meta.data[i][valueScale.id] = valueScale._parse(data[i]); + parsed = meta.data[i]._parsed; + parsed[valueScale.id] = y = valueScale._parse(data[i]); if (indexScale !== valueScale && i < labels.length) { - meta.data[i][indexScale.id] = indexScale._parse(labels[i]); + parsed[indexScale.id] = x = indexScale._parse(labels[i]); + + // store cross reference values, for stacking + if (indexScale.id === meta.xAxisID) { + xr = y; + y = x; + x = xr; + } + xr = xref.x[x] || (xref.x[x] = []); + yr = xref.y[y] || (xref.y[y] = []); + xr[datasetIndex] = y; + yr[datasetIndex] = x; } } }, @@ -407,7 +442,17 @@ helpers.extend(DatasetController.prototype, { if (index < 0 || index >= data.length) { return NaN; } - return data[index][scale.id]; + return data[index]._parsed[scale.id]; + }, + + /** + * @private + */ + _getParsedValueByValue: function(axis, value) { + var me = this; + var meta = me.getMeta(); + var xref = meta._xref && meta._xref[axis]; + return xref && xref[value] && xref[value][me.index]; }, /** diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 4d30ecb38fe..117c1a339ad 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -100,6 +100,15 @@ function computeTextSize(context, tick, font) { } module.exports = Element.extend({ + + /** + * @private + * @since 2.9 + */ + _getAxis: function() { + return this.isHorizontal() ? 'x' : 'y'; + }, + /** * Function that parses a supported input value to internal representation. * @param {*} raw From d2c9e4d295432004befc0b15b98c3fed91d85d57 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sat, 2 Mar 2019 17:14:12 +0200 Subject: [PATCH 13/16] fix some issues --- src/controllers/controller.bar.js | 7 ++++--- src/scales/scale.linear.js | 23 +++++++++++++++++------ src/scales/scale.time.js | 5 ++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index d4cbc45f21d..22f9f71be06 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -283,11 +283,12 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var meta = me.getMeta(); - var indexValue = me._getParsedValue(index, me._getIndexScale()); var scale = me._getValueScale(); + var indexScale = me._getIndexScale(); var isHorizontal = scale.isHorizontal(); - var axis = scale._getAxis(); var value = me._getParsedValue(index, scale); + var indexValue = me._getParsedValue(index, indexScale); + var axis = indexScale._getAxis(); var minBarLength = scale.options.minBarLength; var stacked = scale.options.stacked; var stack = meta.stack; @@ -300,7 +301,7 @@ module.exports = DatasetController.extend({ if (imeta.bar && imeta.stack === stack && chart.isDatasetVisible(i)) { ivalue = imeta.controller._getParsedValueByValue(axis, indexValue); - if (ivalue !== undefined && ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0))) { + if (((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0))) { start += ivalue; } } diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js index 10ae36c3be2..f5a483b5838 100644 --- a/src/scales/scale.linear.js +++ b/src/scales/scale.linear.js @@ -74,23 +74,34 @@ module.exports = LinearScaleBase.extend({ if (isNaN(value) || meta.data[index].hidden) { return; } + var otherScale = meta.controller.getScaleForId(meta.xAxisID === me.id ? meta.yAxisID : meta.xAxisID); + var ivalue = otherScale._getParsedValue(index, datasetIndex); - positiveValues[index] = positiveValues[index] || 0; - negativeValues[index] = negativeValues[index] || 0; + positiveValues[ivalue] = positiveValues[ivalue] || 0; + negativeValues[ivalue] = negativeValues[ivalue] || 0; if (opts.relativePoints) { - positiveValues[index] = 100; + positiveValues[ivalue] = 100; } else if (value < 0) { - negativeValues[index] += value; + negativeValues[ivalue] += value; } else { - positiveValues[index] += value; + positiveValues[ivalue] += value; } }); } }); helpers.each(valuesPerStack, function(valuesForType) { - var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); + var values = []; + var i, ilen, keys; + keys = Object.keys(valuesForType.positiveValues); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + values.push(valuesForType.positiveValues[keys[i]]); + } + keys = Object.keys(valuesForType.negativeValues); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + values.push(valuesForType.negativeValues[keys[i]]); + } var minVal = helpers.min(values); var maxVal = helpers.max(values); me.min = me.min === null ? minVal : Math.min(me.min, minVal); diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index e43b12cf336..832fe870901 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -479,7 +479,10 @@ module.exports = Scale.extend({ if (obj && obj.t) { return this._parse(obj.t); } - return Scale.prototype._parseObject.call(obj, axis); + if (obj[axis] !== undefined) { + return this._parse(obj[axis]); + } + return null; }, update: function() { From 1f1a93d448c65663b76e6882b03f67dcd6da2c40 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sun, 3 Mar 2019 15:45:43 +0200 Subject: [PATCH 14/16] remove chore from _getStacks, refactor parsing / retrieving parsed values, use parsed custom in bubble (radius) --- src/controllers/controller.bar.js | 12 +-- src/controllers/controller.bubble.js | 7 +- src/core/core.datasetController.js | 155 ++++++++++++++------------- src/core/core.scale.js | 8 -- 4 files changed, 90 insertions(+), 92 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 22f9f71be06..78b0d072127 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -201,16 +201,15 @@ module.exports = DatasetController.extend({ var stacked = scale.options.stacked; var ilen = last === undefined ? chart.data.datasets.length : last + 1; var stacks = []; - var i, meta, stack; + var i, meta; for (i = 0; i < ilen; ++i) { meta = chart.getDatasetMeta(i); - stack = meta.stack; if (meta.bar && chart.isDatasetVisible(i) && (stacked === false || - (stacked === true && stacks.indexOf(stack) === -1) || - (stacked === undefined && (stack === undefined || stacks.indexOf(stack) === -1)))) { - stacks.push(stack); + (stacked === true && stacks.indexOf(meta.stack) === -1) || + (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { + stacks.push(meta.stack); } } @@ -288,7 +287,6 @@ module.exports = DatasetController.extend({ var isHorizontal = scale.isHorizontal(); var value = me._getParsedValue(index, scale); var indexValue = me._getParsedValue(index, indexScale); - var axis = indexScale._getAxis(); var minBarLength = scale.options.minBarLength; var stacked = scale.options.stacked; var stack = meta.stack; @@ -300,7 +298,7 @@ module.exports = DatasetController.extend({ imeta = chart.getDatasetMeta(i); if (imeta.bar && imeta.stack === stack && chart.isDatasetVisible(i)) { - ivalue = imeta.controller._getParsedValueByValue(axis, indexValue); + ivalue = imeta.controller._getParsedValueByScaleValue(indexValue, indexScale); if (((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0))) { start += ivalue; } diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index b7f32403ac1..3ebc5ece43d 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -45,8 +45,8 @@ module.exports = DatasetController.extend({ /** * @private */ - _parseCustomObjectData: function(obj, metaData) { - metaData.r = +obj.r; + _parseCustomObjectData: function(obj) { + return obj && obj.r && +obj.r; }, /** @@ -135,7 +135,6 @@ module.exports = DatasetController.extend({ var dataset = datasets[me.index]; var custom = point.custom || {}; var options = chart.options.elements.point; - var data = dataset.data[index]; var values = {}; var i, ilen, key; @@ -172,7 +171,7 @@ module.exports = DatasetController.extend({ // Custom radius resolution values.radius = resolve([ custom.radius, - data ? data.r : undefined, + me._getParsedCustom(index), dataset.radius, options.radius ], context, index); diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 8589133d50f..76a41cdfd2e 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -75,6 +75,48 @@ function unlistenArrayEvents(array, listener) { delete array._chartjs; } +function storeParsedData(meta, index, xid, yid, xvalue, yvalue, custom) { + var metaData = meta.data[index]; + var datasetIndex = metaData._datasetIndex; + var parsed = metaData._parsed || (metaData._parsed = {}); + var crossRef = meta._xref || (meta._xref = {}); + var xref = crossRef[xid] || (crossRef[xid] = {}); + var yref = crossRef[yid] || (crossRef[yid] = {}); + var xvref = xref[xvalue] || (xref[xvalue] = []); + var yvref = yref[yvalue] || (yref[yvalue] = []); + + parsed[xid] = xvalue; + parsed[yid] = yvalue; + parsed._custom = custom; + + xvref[datasetIndex] = yvalue; + yvref[datasetIndex] = xvalue; +} + +function loadParsedData(meta, index, scaleId) { + var metaData = meta && meta.data || []; + return index >= 0 + && index < metaData.length + && metaData[index]._parsed + && metaData[index]._parsed[scaleId]; +} + +function loadParsedDataByScaleValue(meta, value, scaleId, datasetIndex) { + return meta + && meta._xref + && meta._xref[scaleId] + && meta._xref[scaleId][value] + && meta._xref[scaleId][value][datasetIndex]; +} + +function loadCustomData(meta, index) { + var metaData = meta && meta.data || []; + return index >= 0 + && index < metaData.length + && metaData[index]._parsed + && metaData[index]._parsed._custom; +} + // Base class for all dataset controllers (line, bar, etc) var DatasetController = function(chart, datasetIndex) { this.initialize(chart, datasetIndex); @@ -328,15 +370,16 @@ helpers.extend(DatasetController.prototype, { */ _parse: function(start, count) { var me = this; + var meta = me.getMeta(); var data = me.getDataset().data; if (data && data.length) { if (helpers.isArray(data[0])) { - me._parseArrayData(start, count); + me._parseArrayData(data, meta, start, count); } else if (helpers.isObject(data[0])) { - me._parseObjectData(start, count); + me._parseObjectData(data, meta, start, count); } else { - me._parsePlainData(start, count); + me._parsePlainData(data, meta, start, count); } } }, @@ -344,28 +387,17 @@ helpers.extend(DatasetController.prototype, { /** * @private */ - _parseArrayData: function(start, count) { + _parseArrayData: function(data, meta, start, count) { var me = this; - var datasetIndex = me.index; - var meta = me.getMeta(); - var xref = meta._xref || (meta._xref = {x: {}, y: {}}); - var data = me.getDataset().data; var xID = meta.xAxisID; var yID = meta.yAxisID; var xScale = me.getScaleForId(xID); var yScale = me.getScaleForId(yID); - var i, ilen, v, parsed, x, y, xr, yr; + var i, ilen, x, y; for (i = start, ilen = start + count; i < ilen; ++i) { - parsed = meta.data[i]._parsed; - v = data[i]; - parsed[xID] = x = xScale._parse(v[0]); - parsed[yID] = y = yScale._parse(v[1]); - - // store cross reference values, for stacking - xr = xref.x[x] || (xref.x[x] = []); - yr = xref.y[y] || (xref.y[y] = []); - xr[datasetIndex] = y; - yr[datasetIndex] = x; + x = xScale._parse(data[i][0]); + y = yScale._parse(data[i][1]); + storeParsedData(meta, i, xID, yID, x, y); } }, @@ -374,63 +406,40 @@ helpers.extend(DatasetController.prototype, { /** * @private */ - _parseObjectData: function(start, count) { + _parseObjectData: function(data, meta, start, count) { var me = this; - var datasetIndex = me.index; - var meta = me.getMeta(); - var xref = meta._xref || (meta._xref = {x: {}, y: {}}); - var data = me.getDataset().data; var xID = meta.xAxisID; var yID = meta.yAxisID; - var xScale = me.getScaleForId(meta.xAxisID); - var yScale = me.getScaleForId(meta.yAxisID); - var i, ilen, v, parsed, x, y, xr, yr; + var xScale = me.getScaleForId(xID); + var yScale = me.getScaleForId(yID); + var i, ilen, x, y, c; for (i = start, ilen = start + count; i < ilen; ++i) { - parsed = meta.data[i]._parsed; - v = data[i]; - parsed[xID] = x = xScale._parseObject(v, 'x', i); - parsed[yID] = y = yScale._parseObject(data[i], 'y', i); - me._parseCustomObjectData(data[i], meta.data[i]); - - // store cross reference values, for stacking - xr = xref.x[x] || (xref.x[x] = []); - yr = xref.y[y] || (xref.y[y] = []); - xr[datasetIndex] = y; - yr[datasetIndex] = x; + x = xScale._parseObject(data[i], 'x', i); + y = yScale._parseObject(data[i], 'y', i); + c = me._parseCustomObjectData(data[i]); + storeParsedData(meta, i, xID, yID, x, y, c); } }, /** * @private */ - _parsePlainData: function(start, count) { + _parsePlainData: function(data, meta, start, count) { var me = this; - var datasetIndex = me.index; - var meta = me.getMeta(); - var xref = meta._xref || (meta._xref = {x: {}, y: {}}); - var data = me.getDataset().data; - var indexScale = me._getIndexScale(); - var valueScale = me._getValueScale(); - var labels = indexScale._getLabels() || []; - var i, ilen, parsed, x, y, xr, yr; + var iScale = me._getIndexScale(); + var vScale = me._getValueScale(); + var iID = iScale.id; + var vID = vScale.id; + + var labels = iScale._getLabels() || []; + var i, ilen, iv, v; for (i = start, ilen = start + count; i < ilen; ++i) { - parsed = meta.data[i]._parsed; - parsed[valueScale.id] = y = valueScale._parse(data[i]); - if (indexScale !== valueScale && i < labels.length) { - parsed[indexScale.id] = x = indexScale._parse(labels[i]); - - // store cross reference values, for stacking - if (indexScale.id === meta.xAxisID) { - xr = y; - y = x; - x = xr; - } - xr = xref.x[x] || (xref.x[x] = []); - yr = xref.y[y] || (xref.y[y] = []); - xr[datasetIndex] = y; - yr[datasetIndex] = x; - } + v = vScale._parse(data[i]); + iv = iScale !== vScale && i < labels.length + ? iScale._parse(labels[i]) + : i; + storeParsedData(meta, i, iID, vID, iv, v); } }, @@ -438,21 +447,21 @@ helpers.extend(DatasetController.prototype, { * @private */ _getParsedValue: function(index, scale) { - var data = this.getMeta().data; - if (index < 0 || index >= data.length) { - return NaN; - } - return data[index]._parsed[scale.id]; + return loadParsedData(this.getMeta(), index, scale.id); }, /** * @private */ - _getParsedValueByValue: function(axis, value) { - var me = this; - var meta = me.getMeta(); - var xref = meta._xref && meta._xref[axis]; - return xref && xref[value] && xref[value][me.index]; + _getParsedCustom: function(index) { + return loadCustomData(this.getMeta(), index); + }, + + /** + * @private + */ + _getParsedValueByScaleValue: function(value, scale) { + return loadParsedDataByScaleValue(this.getMeta(), value, scale.id, this.index); }, /** diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 117c1a339ad..aea8e66bb4e 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -101,14 +101,6 @@ function computeTextSize(context, tick, font) { module.exports = Element.extend({ - /** - * @private - * @since 2.9 - */ - _getAxis: function() { - return this.isHorizontal() ? 'x' : 'y'; - }, - /** * Function that parses a supported input value to internal representation. * @param {*} raw From 2a18b623697f3c1774482d763088956a8d2cb322 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 15 Mar 2019 20:42:16 +0200 Subject: [PATCH 15/16] remove hardcoding of scale id --- src/controllers/controller.polarArea.js | 5 ++--- src/controllers/controller.radar.js | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 836446860d7..5658ee5187d 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -9,7 +9,6 @@ var resolve = helpers.options.resolve; defaults._set('polarArea', { scale: { - id: 'radial', type: 'radialLinear', angleLines: { display: false @@ -118,11 +117,11 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, _getIndexScaleId: function() { - return 'radial'; + return this.chart.scale.id; }, _getValueScaleId: function() { - return 'radial'; + return this.chart.scale.id; }, update: function(reset) { diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index db9a719438d..71f22328fb2 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -10,7 +10,6 @@ var resolve = helpers.options.resolve; defaults._set('radar', { scale: { - id: 'radial', type: 'radialLinear' }, elements: { @@ -29,11 +28,11 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, _getIndexScaleId: function() { - return 'radial'; + return this.chart.scale.id; }, _getValueScaleId: function() { - return 'radial'; + return this.chart.scale.id; }, update: function(reset) { From 4997e7fe4e0329264e25f6be3a0d0a70cb1ad6fd Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sat, 16 Mar 2019 09:42:07 +0200 Subject: [PATCH 16/16] Add support for data as object --- src/controllers/controller.bar.js | 2 +- src/core/core.datasetController.js | 76 ++++++++++++++++++++---------- src/core/core.scale.js | 7 +++ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 78b0d072127..514141fa827 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -257,7 +257,7 @@ module.exports = DatasetController.extend({ var i, ilen, min; for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { - pixels.push(scale.getPixelForValue(null, i, datasetIndex)); + pixels.push(scale._getPixelForIndex(i, datasetIndex)); } min = helpers.isNullOrUndef(scale.options.barThickness) diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 76a41cdfd2e..8e6d68878fb 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -117,6 +117,21 @@ function loadCustomData(meta, index) { && metaData[index]._parsed._custom; } +function convertObjectDataToArray(data) { + var keys = Object.keys(data); + var adata = []; + var i, ilen, key; + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + adata.push({ + x: key, + y: data[key] + }); + } + return adata; +} + + // Base class for all dataset controllers (line, bar, etc) var DatasetController = function(chart, datasetIndex) { this.initialize(chart, datasetIndex); @@ -234,12 +249,43 @@ helpers.extend(DatasetController.prototype, { }); }, + _dataCheck: function() { + var me = this; + var dataset = me.getDataset(); + var data = dataset.data || (dataset.data = []); + + // In order to correctly handle data addition/deletion animation (an thus simulate + // real-time charts), we need to monitor these data modifications and synchronize + // the internal meta data accordingly. + if (me._data !== data) { + if (helpers.isObject(data)) { + // Object data is currently monitored for replacement only + if (me._objectData !== data) { + me._data = convertObjectDataToArray(data); + me._objectData = data; + } + } else { + if (me._data) { + // This case happens when the user replaced the data array instance. + unlistenArrayEvents(me._data, me); + } + + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, me); + } + me._data = data; + } + } + }, + addElements: function() { var me = this; var meta = me.getMeta(); - var data = me.getDataset().data || []; var metaData = meta.data; - var i, ilen; + var i, ilen, data; + + me._dataCheck(); + data = me._data; for (i = 0, ilen = data.length; i < ilen; ++i) { metaData[i] = metaData[i] || me.createMetaData(i); @@ -256,28 +302,11 @@ helpers.extend(DatasetController.prototype, { }, buildOrUpdateElements: function() { - var me = this; - var dataset = me.getDataset(); - var data = dataset.data || (dataset.data = []); - - // In order to correctly handle data addition/deletion animation (an thus simulate - // real-time charts), we need to monitor these data modifications and synchronize - // the internal meta data accordingly. - if (me._data !== data) { - if (me._data) { - // This case happens when the user replaced the data array instance. - unlistenArrayEvents(me._data, me); - } - - if (data && Object.isExtensible(data)) { - listenArrayEvents(data, me); - } - me._data = data; - } + this._dataCheck(); // Re-sync meta data in case the user replaced the data array or if we missed // any updates and so make sure that we handle number of datapoints changing. - me.resyncElements(); + this.resyncElements(); }, update: helpers.noop, @@ -341,9 +370,8 @@ helpers.extend(DatasetController.prototype, { resyncElements: function() { var me = this; var meta = me.getMeta(); - var data = me.getDataset().data; + var numData = me._data.length; var numMeta = meta.data.length; - var numData = data.length; if (numData < numMeta) { meta.data.splice(numData, numMeta - numData); @@ -371,7 +399,7 @@ helpers.extend(DatasetController.prototype, { _parse: function(start, count) { var me = this; var meta = me.getMeta(); - var data = me.getDataset().data; + var data = me._data; if (data && data.length) { if (helpers.isArray(data[0])) { diff --git a/src/core/core.scale.js b/src/core/core.scale.js index aea8e66bb4e..b9c02da3670 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -584,6 +584,13 @@ module.exports = Element.extend({ */ _getPixel: helpers.noop, + /** + * @private + */ + _getPixelForIndex: function(index, datasetIndex) { + return this._getPixel(this._getParsedValue(index, datasetIndex)); + }, + /** * Used to get the value to display in the tooltip for the data at the given index * @param index