From: Jukka Kurkela Date: Mon, 6 Jan 2020 12:22:18 +0000 (+0200) Subject: Internal data by axis instead of scale id (#6912) X-Git-Tag: v3.0.0-alpha~155 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5d5ed987aedf95aea33d5fe8d6305f1d819b002;p=thirdparty%2FChart.js.git Internal data by axis instead of scale id (#6912) * Internal data by axis instead of scale id * Test * Update test * Update docs --- diff --git a/docs/general/data-structures.md b/docs/general/data-structures.md index 70bed1cc2..aead2af63 100644 --- a/docs/general/data-structures.md +++ b/docs/general/data-structures.md @@ -25,7 +25,7 @@ data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}] data: [{x:'Sales', y:20}, {x:'Revenue', y:10}] ``` -This is also the internal format used for parsed data. Property names are matched to scale-id. In this mode, parsing can be disabled by specifying `parsing: false` at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally. +This is also the internal format used for parsed data. In this mode, parsing can be disabled by specifying `parsing: false` at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally. ## Object diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 2326d0b94..7a72ce29e 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -144,7 +144,7 @@ function parseFloatBar(arr, item, vScale, i) { // Store `barEnd` (furthest away from origin) as parsed value, // to make stacking straight forward - item[vScale.id] = barEnd; + item[vScale.axis] = barEnd; item._custom = { barStart: barStart, @@ -167,12 +167,12 @@ function parseArrayOrPrimitive(meta, data, start, count) { for (i = start, ilen = start + count; i < ilen; ++i) { entry = data[i]; item = {}; - item[iScale.id] = singleScale || iScale._parse(labels[i], i); + item[iScale.axis] = singleScale || iScale._parse(labels[i], i); if (helpers.isArray(entry)) { parseFloatBar(entry, item, vScale, i); } else { - item[vScale.id] = vScale._parse(entry, i); + item[vScale.axis] = vScale._parse(entry, i); } parsed.push(item); @@ -230,12 +230,12 @@ module.exports = DatasetController.extend({ for (i = start, ilen = start + count; i < ilen; ++i) { obj = data[i]; item = {}; - item[iScale.id] = iScale._parseObject(obj, iScale.axis, i); + item[iScale.axis] = iScale._parseObject(obj, iScale.axis, i); value = obj[vProp]; if (helpers.isArray(value)) { parseFloatBar(value, item, vScale, i); } else { - item[vScale.id] = vScale._parseObject(obj, vProp, i); + item[vScale.axis] = vScale._parseObject(obj, vProp, i); } parsed.push(item); } @@ -253,10 +253,10 @@ module.exports = DatasetController.extend({ const custom = parsed._custom; const value = custom ? '[' + custom.start + ', ' + custom.end + ']' - : '' + vScale.getLabelForValue(parsed[vScale.id]); + : '' + vScale.getLabelForValue(parsed[vScale.axis]); return { - label: '' + iScale.getLabelForValue(parsed[iScale.id]), + label: '' + iScale.getLabelForValue(parsed[iScale.axis]), value: value }; }, @@ -394,7 +394,7 @@ module.exports = DatasetController.extend({ let i, ilen; for (i = 0, ilen = meta.data.length; i < ilen; ++i) { - pixels.push(iScale.getPixelForValue(me._getParsed(i)[iScale.id])); + pixels.push(iScale.getPixelForValue(me._getParsed(i)[iScale.axis])); } return { @@ -417,9 +417,9 @@ module.exports = DatasetController.extend({ const minBarLength = options.minBarLength; const parsed = me._getParsed(index); const custom = parsed._custom; - let value = parsed[vScale.id]; + let value = parsed[vScale.axis]; let start = 0; - let length = meta._stacked ? me._applyStack(vScale, parsed) : parsed[vScale.id]; + let length = meta._stacked ? me._applyStack(vScale, parsed) : parsed[vScale.axis]; let base, head, size; if (length !== value) { @@ -489,7 +489,7 @@ module.exports = DatasetController.extend({ helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { - if (!isNaN(me._getParsed(i)[vScale.id])) { + if (!isNaN(me._getParsed(i)[vScale.axis])) { rects[i].draw(me._ctx); } } diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index efb453bce..70e934afe 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -59,15 +59,13 @@ module.exports = DatasetController.extend({ */ _parseObjectData: function(meta, data, start, count) { const {xScale, yScale} = meta; - const xId = xScale.id; - const yId = yScale.id; const parsed = []; let i, ilen, item; for (i = start, ilen = start + count; i < ilen; ++i) { item = data[i]; parsed.push({ - [xId]: xScale._parseObject(item, 'x', i), - [yId]: yScale._parseObject(item, 'y', i), + x: xScale._parseObject(item, 'x', i), + y: yScale._parseObject(item, 'y', i), _custom: item && item.r && +item.r }); } @@ -96,8 +94,8 @@ module.exports = DatasetController.extend({ const meta = me._cachedMeta; const {xScale, yScale} = meta; const parsed = me._getParsed(index); - const x = xScale.getLabelForValue(parsed[xScale.id]); - const y = yScale.getLabelForValue(parsed[yScale.id]); + const x = xScale.getLabelForValue(parsed.x); + const y = yScale.getLabelForValue(parsed.y); const r = parsed._custom; return { @@ -133,8 +131,8 @@ module.exports = DatasetController.extend({ const point = points[i]; const index = start + i; const parsed = !reset && me._getParsed(index); - const x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(parsed[xScale.id]); - const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed[yScale.id]); + const x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(parsed.x); + const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed.y); const properties = { x, y, diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index dd8eded5b..d56f04cf3 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -104,8 +104,8 @@ module.exports = DatasetController.extend({ const index = start + i; const point = points[i]; const parsed = me._getParsed(index); - const x = xScale.getPixelForValue(parsed[xScale.id]); - const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(_stacked ? me._applyStack(yScale, parsed) : parsed[yScale.id]); + const x = xScale.getPixelForValue(parsed.x); + const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(_stacked ? me._applyStack(yScale, parsed) : parsed.y); const properties = { x, y, diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index ab1f18c1c..834ff698f 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -81,7 +81,7 @@ module.exports = DatasetController.extend({ return { label: vScale._getLabels()[index], - value: '' + vScale.getLabelForValue(parsed[vScale.id]) + value: '' + vScale.getLabelForValue(parsed[vScale.axis]) }; }, diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 8ed4310f5..ec5f260ca 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -193,17 +193,17 @@ function updateStacks(controller, parsed) { const {chart, _cachedMeta: meta} = controller; const stacks = chart._stacks || (chart._stacks = {}); // map structure is {stackKey: {datasetIndex: value}} const {iScale, vScale, index: datasetIndex} = meta; - const iId = iScale.id; - const vId = vScale.id; + const iAxis = iScale.axis; + const vAxis = vScale.axis; const key = getStackKey(iScale, vScale, meta); const ilen = parsed.length; let stack; for (let i = 0; i < ilen; ++i) { const item = parsed[i]; - const {[iId]: index, [vId]: value} = item; + const {[iAxis]: index, [vAxis]: value} = item; const itemStacks = item._stacks || (item._stacks = {}); - stack = itemStacks[vId] = getOrCreateStack(stacks, key, index); + stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index); stack[datasetIndex] = value; } } @@ -478,7 +478,7 @@ helpers.extend(DatasetController.prototype, { const me = this; const {_cachedMeta: meta, _data: data} = me; const {iScale, vScale, _stacked} = meta; - const iScaleId = iScale.id; + const iAxis = iScale.axis; let sorted = true; let i, parsed, cur, prev; @@ -503,7 +503,7 @@ helpers.extend(DatasetController.prototype, { for (i = 0; i < count; ++i) { meta._parsed[i + start] = cur = parsed[i]; if (sorted) { - if (prev && cur[iScaleId] < prev[iScaleId]) { + if (prev && cur[iAxis] < prev[iAxis]) { sorted = false; } prev = cur; @@ -533,8 +533,8 @@ helpers.extend(DatasetController.prototype, { */ _parsePrimitiveData: function(meta, data, start, count) { const {iScale, vScale} = meta; - const iId = iScale.id; - const vId = vScale.id; + const iAxis = iScale.axis; + const vAxis = vScale.axis; const labels = iScale._getLabels(); const singleScale = iScale === vScale; const parsed = new Array(count); @@ -543,8 +543,8 @@ helpers.extend(DatasetController.prototype, { for (i = 0, ilen = count; i < ilen; ++i) { index = i + start; parsed[i] = { - [iId]: singleScale || iScale._parse(labels[index], index), - [vId]: vScale._parse(data[index], index) + [iAxis]: singleScale || iScale._parse(labels[index], index), + [vAxis]: vScale._parse(data[index], index) }; } return parsed; @@ -558,13 +558,11 @@ helpers.extend(DatasetController.prototype, { * @param {number} count - number of items to parse * @returns {object} parsed item - item containing index and a parsed value * for each scale id. - * Example: {xScale0: 0, yScale0: 1} + * Example: {x: 0, y: 1} * @private */ _parseArrayData: function(meta, data, start, count) { const {xScale, yScale} = meta; - const xId = xScale.id; - const yId = yScale.id; const parsed = new Array(count); let i, ilen, index, item; @@ -572,8 +570,8 @@ helpers.extend(DatasetController.prototype, { index = i + start; item = data[index]; parsed[i] = { - [xId]: xScale._parse(item[0], index), - [yId]: yScale._parse(item[1], index) + x: xScale._parse(item[0], index), + y: yScale._parse(item[1], index) }; } return parsed; @@ -592,8 +590,6 @@ helpers.extend(DatasetController.prototype, { */ _parseObjectData: function(meta, data, start, count) { const {xScale, yScale} = meta; - const xId = xScale.id; - const yId = yScale.id; const parsed = new Array(count); let i, ilen, index, item; @@ -601,8 +597,8 @@ helpers.extend(DatasetController.prototype, { index = i + start; item = data[index]; parsed[i] = { - [xId]: xScale._parseObject(item, 'x', index), - [yId]: yScale._parseObject(item, 'y', index) + x: xScale._parseObject(item, 'x', index), + y: yScale._parseObject(item, 'y', index) }; } return parsed; @@ -612,11 +608,7 @@ helpers.extend(DatasetController.prototype, { * @private */ _getParsed: function(index) { - const data = this._cachedMeta._parsed; - if (index < 0 || index >= data.length) { - return; - } - return data[index]; + return this._cachedMeta._parsed[index]; }, /** @@ -625,10 +617,10 @@ helpers.extend(DatasetController.prototype, { _applyStack: function(scale, parsed) { const chart = this.chart; const meta = this._cachedMeta; - const value = parsed[scale.id]; + const value = parsed[scale.axis]; const stack = { keys: getSortedDatasetIndices(chart, true), - values: parsed._stacks[scale.id] + values: parsed._stacks[scale.axis] }; return applyStack(stack, value, meta.index); }, @@ -651,7 +643,7 @@ helpers.extend(DatasetController.prototype, { function _compute() { if (stack) { - stack.values = parsed._stacks[scale.id]; + stack.values = parsed._stacks[scale.axis]; // Need to consider individual stack values for data range, // in addition to the stacked value min = Math.min(min, value); @@ -668,8 +660,8 @@ helpers.extend(DatasetController.prototype, { function _skip() { item = data[i]; parsed = _parsed[i]; - value = parsed[scale.id]; - otherValue = parsed[otherScale.id]; + value = parsed[scale.axis]; + otherValue = parsed[otherScale.axis]; return ((item && item.hidden) || isNaN(value) || otherMin > otherValue || otherMax < otherValue); } @@ -703,7 +695,7 @@ helpers.extend(DatasetController.prototype, { let i, ilen, value; for (i = 0, ilen = parsed.length; i < ilen; ++i) { - value = parsed[i][scale.id]; + value = parsed[i][scale.axis]; if (!isNaN(value)) { values.push(value); } @@ -759,8 +751,8 @@ helpers.extend(DatasetController.prototype, { const vScale = meta.vScale; const parsed = me._getParsed(index); return { - label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.id]) : '', - value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.id]) : '' + label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '', + value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : '' }; }, diff --git a/test/specs/core.datasetController.tests.js b/test/specs/core.datasetController.tests.js index ca7182eb3..37545d79b 100644 --- a/test/specs/core.datasetController.tests.js +++ b/test/specs/core.datasetController.tests.js @@ -118,6 +118,55 @@ describe('Chart.DatasetController', function() { }); }); + it('should parse data using correct scales', function() { + const data1 = [0, 1, 2, 3, 4, 5]; + const data2 = ['a', 'b', 'c', 'd', 'a']; + const chart = acquireChart({ + type: 'line', + data: { + datasets: [ + {data: data1}, + {data: data2, xAxisID: 'x2', yAxisID: 'y2'} + ] + }, + options: { + scales: { + x: { + type: 'category', + labels: ['one', 'two', 'three', 'four', 'five', 'six'] + }, + x2: { + type: 'logarithmic', + labels: ['1', '10', '100', '1000', '2000'] + }, + y: { + type: 'linear' + }, + y2: { + type: 'category', + labels: ['a', 'b', 'c', 'd', 'e'] + } + } + } + }); + + const meta1 = chart.getDatasetMeta(0); + const parsedXValues1 = meta1._parsed.map(p => p.x); + const parsedYValues1 = meta1._parsed.map(p => p.y); + + expect(meta1.data.length).toBe(6); + expect(parsedXValues1).toEqual([0, 1, 2, 3, 4, 5]); // label indices + expect(parsedYValues1).toEqual(data1); + + const meta2 = chart.getDatasetMeta(1); + const parsedXValues2 = meta2._parsed.map(p => p.x); + const parsedYValues2 = meta2._parsed.map(p => p.y); + + expect(meta2.data.length).toBe(5); + expect(parsedXValues2).toEqual([1, 10, 100, 1000, 2000]); // logarithmic scale labels + expect(parsedYValues2).toEqual([0, 1, 2, 3, 0]); // label indices + }); + it('should synchronize metadata when data are inserted or removed and parsing is on', function() { const data = [0, 1, 2, 3, 4, 5]; const chart = acquireChart({