Skip to content

Commit 969e829

Browse files
committed
Add decimation plugin to the core
1 parent 1ad5ab9 commit 969e829

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

src/plugins/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export {default as Decimation} from './plugin.decimation';
12
export {default as Filler} from './plugin.filler';
23
export {default as Legend} from './plugin.legend';
34
export {default as Title} from './plugin.title';

src/plugins/plugin.decimation.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
export default {
2+
id: 'decimation',
3+
4+
beforeElementsUpdate: (chart, args, options) => {
5+
if (!options.enabled) {
6+
return;
7+
}
8+
9+
// Use an approximation since this is before scales are parsed
10+
const verticalAxisCount = Object.values(chart.scales).reduce((acc, scale) => acc + scale.axis === 'y' ? 1 : 0, 0);
11+
12+
// assume ~50px for now. If the axis is actually wider then we have more points than needed
13+
// TODO: Take display settings into account and compute a more accurate guess for each axis
14+
const availableWidth = chart.width - (verticalAxisCount * 50);
15+
16+
chart.data.datasets.forEach((dataset, datasetIndex) => {
17+
let i, point, x, y, prevX, minIndex, maxIndex, minY, maxY;
18+
const {data} = dataset;
19+
const meta = chart.getDatasetMeta(datasetIndex);
20+
21+
if (meta.type !== 'line') {
22+
// Only line datasets are supported
23+
return;
24+
}
25+
26+
const xAxis = chart.scales[meta.xAxisID];
27+
if (xAxis.type !== 'linear' && xAxis.type !== 'time') {
28+
// Only linear interpolation is supported
29+
return;
30+
}
31+
32+
if (chart.options.parsing) {
33+
// Plugin only supports data that does not need parsing
34+
return;
35+
}
36+
37+
if (data.length <= 4 * availableWidth) {
38+
// No decimation is required until we are above this threshold
39+
return;
40+
}
41+
42+
const decimated = [];
43+
44+
const xMin = data[0].x;
45+
const xMax = data[data.length - 1].x;
46+
const dx = xMax - xMin;
47+
48+
for (i = 0; i < data.length; ++i) {
49+
point = data[i];
50+
x = (point.x - xMin) / dx * availableWidth;
51+
y = point.y;
52+
const truncX = x | 0;
53+
54+
if (truncX === prevX) {
55+
// Determine `minY` / `maxY` and `avgX` while we stay within same x-position
56+
if (y < minY) {
57+
minY = y;
58+
minIndex = i;
59+
} else if (y > maxY) {
60+
maxY = y;
61+
maxIndex = i;
62+
}
63+
} else {
64+
// Push up to 4 points, 3 for the last interval and the first point for this interval
65+
if (minIndex && maxIndex) {
66+
decimated.push(data[minIndex], data[maxIndex]);
67+
}
68+
if (i > 0) {
69+
// Last point in the previous interval
70+
decimated.push(data[i - 1]);
71+
}
72+
decimated.push(point);
73+
prevX = truncX;
74+
minY = maxY = y;
75+
minIndex = maxIndex = i;
76+
}
77+
}
78+
79+
dataset._data = dataset.data;
80+
dataset.data = decimated;
81+
});
82+
}
83+
};

0 commit comments

Comments
 (0)