Internal data by axis instead of scale id (#6912)

* Internal data by axis instead of scale id

* Test

* Update test

* Update docs
This commit is contained in:
Jukka Kurkela 2020-01-06 14:22:18 +02:00 committed by Evert Timberg
parent 224fc11500
commit b5d5ed987a
7 changed files with 94 additions and 55 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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])
};
},

View File

@ -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]) : ''
};
},

View File

@ -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({