Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LTTB Decimation #8468

Merged
merged 2 commits into from
Feb 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/docs/configuration/decimation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ Namespace: `options.plugins.decimation`, the global options for the plugin are d
| ---- | ---- | ------- | -----------
| `enabled` | `boolean` | `true` | Is decimation enabled?
| `algorithm` | `string` | `'min-max'` | Decimation algorithm to use. See the [more...](#decimation-algorithms)
| `samples` | `number` | | If the `'lttb'` algorithm is used, this is the number of samples in the output dataset. Defaults to the canvas width to pick 1 sample per pixel.

## Decimation Algorithms

Decimation algorithm to use for data. Options are:

* `'lttb'`
* `'min-max'`

### Largest Triangle Three Bucket (LTTB) Decimation

[LTTB](https://github.com/sveinn-steinarsson/flot-downsample) decimation reduces the number of data points significantly. This is most useful for showing trends in data using only a few data points.

### Min/Max Decimation

[Min/max](https://digital.ni.com/public.nsf/allkb/F694FFEEA0ACF282862576020075F784) decimation will preserve peaks in your data but could require up to 4 points for each pixel. This type of decimation would work well for a very noisy signal where you need to see data peaks.
Expand Down
71 changes: 71 additions & 0 deletions src/plugins/plugin.decimation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,73 @@
import {isNullOrUndef, resolve} from '../helpers';

function lttbDecimation(data, availableWidth, options) {
/**
* Implementation of the Largest Triangle Three Buckets algorithm.
*
* This implementation is based on the original implementation by Sveinn Steinarsson
* in https://github.com/sveinn-steinarsson/flot-downsample/blob/master/jquery.flot.downsample.js
*
* The original implementation is MIT licensed.
*/
const samples = options.samples || availableWidth;
const decimated = [];

const bucketWidth = (data.length - 2) / (samples - 2);
let sampledIndex = 0;
let a = 0;
let i, maxAreaPoint, maxArea, area, nextA;
decimated[sampledIndex++] = data[a];

for (i = 0; i < samples - 2; i++) {
let avgX = 0;
let avgY = 0;
let j;
const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1;
const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, data.length);
const avgRangeLength = avgRangeEnd - avgRangeStart;

for (j = avgRangeStart; j < avgRangeEnd; j++) {
avgX = data[j].x;
avgY = data[j].y;
}

avgX /= avgRangeLength;
avgY /= avgRangeLength;

const rangeOffs = Math.floor(i * bucketWidth) + 1;
const rangeTo = Math.floor((i + 1) * bucketWidth) + 1;
const {x: pointAx, y: pointAy} = data[a];

// Note that this is changed from the original algorithm which initializes these
// values to 1. The reason for this change is that if the area is small, nextA
// would never be set and thus a crash would occur in the next loop as `a` would become
// `undefined`. Since the area is always positive, but could be 0 in the case of a flat trace,
// initializing with a negative number is the correct solution.
maxArea = area = -1;

for (j = rangeOffs; j < rangeTo; j++) {
area = 0.5 * Math.abs(
(pointAx - avgX) * (data[j].y - pointAy) -
(pointAx - data[j].x) * (avgY - pointAy)
);

if (area > maxArea) {
maxArea = area;
maxAreaPoint = data[j];
nextA = j;
}
}

decimated[sampledIndex++] = maxAreaPoint;
a = nextA;
}

// Include the last point
decimated[sampledIndex++] = data[data.length - 1];

return decimated;
}

function minMaxDecimation(data, availableWidth) {
let avgX = 0;
let countX = 0;
Expand Down Expand Up @@ -141,6 +209,9 @@ export default {
// Point the chart to the decimated data
let decimated;
switch (options.algorithm) {
case 'lttb':
decimated = lttbDecimation(data, availableWidth, options);
break;
case 'min-max':
decimated = minMaxDecimation(data, availableWidth);
break;
Expand Down
16 changes: 13 additions & 3 deletions types/index.esm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1920,14 +1920,24 @@ export class BasicPlatform extends BasePlatform {}
export class DomPlatform extends BasePlatform {}

export declare enum DecimationAlgorithm {
lttb = 'lttb',
minmax = 'min-max',
}

export interface DecimationOptions {
interface BaseDecimationOptions {
enabled: boolean;
algorithm: DecimationAlgorithm;
}

interface LttbDecimationOptions extends BaseDecimationOptions {
algorithm: DecimationAlgorithm.lttb;
samples?: number;
}

interface MinMaxDecimationOptions extends BaseDecimationOptions {
algorithm: DecimationAlgorithm.minmax;
}

export type DecimationOptions = LttbDecimationOptions | MinMaxDecimationOptions;

export const Filler: Plugin;
export interface FillerOptions {
propagate: boolean;
Expand Down