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

Timeseries: support panning #9345

Merged
merged 4 commits into from Jul 3, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 32 additions & 36 deletions src/scales/scale.timeseries.js
@@ -1,6 +1,5 @@
import TimeScale from './scale.time';
import {_lookup} from '../helpers/helpers.collection';
import {isNullOrUndef} from '../helpers/helpers.core';

/**
* Linearly interpolates the given source `val` using the table. If value is out of bounds, values
Expand All @@ -11,20 +10,31 @@ import {isNullOrUndef} from '../helpers/helpers.core';
* @return {object}
*/
function interpolate(table, val, reverse) {
const maxIndex = table.length - 1;
let prevSource, nextSource, prevTarget, nextTarget;

// Note: the lookup table ALWAYS contains at least 2 items (min and max)
if (reverse) {
prevSource = Math.floor(val);
nextSource = Math.ceil(val);
prevSource = 0;
nextSource = maxIndex;
if (val > prevSource && val < nextSource) {
prevSource = Math.floor(val);
nextSource = prevSource === val ? val + 1 : Math.ceil(val);
}
prevTarget = table[prevSource];
nextTarget = table[nextSource];
} else {
const result = _lookup(table, val);
prevTarget = result.lo;
nextTarget = result.hi;
prevSource = table[prevTarget];
nextSource = table[nextTarget];
prevTarget = 0;
nextTarget = maxIndex;
prevSource = table[0];
nextSource = table[maxIndex];
if (val >= prevSource && val <= nextSource) {
const result = _lookup(table, val);
prevTarget = result.lo;
nextTarget = result.hi;
prevSource = table[prevTarget];
nextSource = table[nextTarget];
}
}

const span = nextSource - prevSource;
Expand All @@ -42,7 +52,9 @@ class TimeSeriesScale extends TimeScale {
/** @type {object[]} */
this._table = [];
/** @type {number} */
this._maxIndex = undefined;
this._minPos = undefined;
/** @type {number} */
this._tableRange = undefined;
}

/**
Expand All @@ -51,8 +63,9 @@ class TimeSeriesScale extends TimeScale {
initOffsets() {
const me = this;
const timestamps = me._getTimestampsForTable();
me._table = me.buildLookupTable(timestamps);
me._maxIndex = me._table.length - 1;
const table = me._table = me.buildLookupTable(timestamps);
me._minPos = interpolate(table, me.min);
me._tableRange = interpolate(table, me.max) - me._minPos;
super.initOffsets(timestamps);
}

Expand All @@ -70,24 +83,20 @@ class TimeSeriesScale extends TimeScale {
buildLookupTable(timestamps) {
const me = this;
const {min, max} = me;
if (!timestamps.length) {
return [
{time: min, pos: 0},
{time: max, pos: 1}
];
}

const items = [min];
const items = [];
let i, ilen, curr;

for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
curr = timestamps[i];
if (curr > min && curr < max) {
if (curr >= min && curr <= max) {
items.push(curr);
}
}

items.push(max);
if (items.length < 2) {
// In case there is less that 2 timestamps between min and max, the scale is defined by min and max
return [min, max];
}

return items;
}
Expand Down Expand Up @@ -119,25 +128,12 @@ class TimeSeriesScale extends TimeScale {
return timestamps;
}

/**
* @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)
* @param {number} [index]
* @return {number}
*/
getPixelForValue(value, index) {
const me = this;
const offsets = me._offsets;
const pos = me._normalized && me._maxIndex > 0 && !isNullOrUndef(index)
? index / me._maxIndex : me.getDecimalForValue(value);
return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
}

/**
* @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)
* @return {number}
*/
getDecimalForValue(value) {
return interpolate(this._table, value) / this._maxIndex;
return (interpolate(this._table, value) - this._minPos) / this._tableRange;
}

/**
Expand All @@ -148,7 +144,7 @@ class TimeSeriesScale extends TimeScale {
const me = this;
const offsets = me._offsets;
const decimal = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
return interpolate(me._table, decimal * this._maxIndex, true);
return interpolate(me._table, decimal * me._tableRange + me._minPos, true);
}
}

Expand Down
Binary file modified test/fixtures/scale.timeseries/source-data-offset-min-max.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/fixtures/scale.timeseries/source-labels-offset-min-max.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/fixtures/scale.timeseries/ticks-reverse-max.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/fixtures/scale.timeseries/ticks-reverse-min-max.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/fixtures/scale.timeseries/ticks-reverse-min.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 4 additions & 8 deletions test/specs/scale.time.tests.js
Expand Up @@ -764,7 +764,7 @@ describe('Time scale tests', function() {
var start = scale.left;
var slice = scale.width / 5;

expect(scale.getPixelForValue(moment('2017').valueOf(), 1)).toBeCloseToPixel(start + slice);
expect(scale.getPixelForValue(moment('2017').valueOf(), 1)).toBeCloseToPixel(86);
expect(scale.getPixelForValue(moment('2042').valueOf(), 5)).toBeCloseToPixel(start + slice * 5);
});
it ('should add a step after if scale.max is after the last data', function() {
Expand All @@ -776,10 +776,9 @@ describe('Time scale tests', function() {
chart.update();

var start = scale.left;
var slice = scale.width / 5;

expect(scale.getPixelForValue(moment('2017').valueOf(), 0)).toBeCloseToPixel(start);
expect(scale.getPixelForValue(moment('2042').valueOf(), 4)).toBeCloseToPixel(start + slice * 4);
expect(scale.getPixelForValue(moment('2042').valueOf(), 4)).toBeCloseToPixel(388);
});
it ('should add steps before and after if scale.min/max are outside the data range', function() {
var chart = this.chart;
Expand All @@ -790,11 +789,8 @@ describe('Time scale tests', function() {
options.max = '2050';
chart.update();

var start = scale.left;
var slice = scale.width / 6;

expect(scale.getPixelForValue(moment('2017').valueOf(), 1)).toBeCloseToPixel(start + slice);
expect(scale.getPixelForValue(moment('2042').valueOf(), 5)).toBeCloseToPixel(start + slice * 5);
expect(scale.getPixelForValue(moment('2017').valueOf(), 1)).toBeCloseToPixel(71);
expect(scale.getPixelForValue(moment('2042').valueOf(), 5)).toBeCloseToPixel(401);
});
});
describe('is "time"', function() {
Expand Down