Skip to content

Commit

Permalink
Simplify lookup, add reverse support
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle committed Jan 16, 2020
1 parent 8303f4b commit a8cc060
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 32 deletions.
33 changes: 20 additions & 13 deletions src/core/core.interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import helpers from '../helpers/index';
import {_isPointInArea} from '../helpers/helpers.canvas';
import {_lookup} from '../helpers/helpers.collection';
import {_lookup, _rlookup} from '../helpers/helpers.collection';

/**
* Helper function to get relative position for an event
Expand Down Expand Up @@ -41,10 +41,22 @@ function evaluateAllVisibleItems(chart, handler) {
}
}

function useBinarySearch(axis, controller, _sorted, intersect) {
/**
* Helper function to do binary search when possible
* @param {object} metaset - the dataset meta
* @param {string} axis - the axis mide. x|y|xy
* @param {number} value - the value to find
* @param {boolean} intersect - should the element intersect
* @returns {lo, hi} indices to search data array between
*/
function binarySearch(metaset, axis, value, intersect) {
const {controller, data, _sorted} = metaset;
const iScale = controller._cachedMeta.iScale;
return iScale && axis === iScale.axis && _sorted &&
(!intersect || controller._elementsShareOptions);
if (iScale && axis === iScale.axis && _sorted &&
(!intersect || controller._elementsShareOptions)) {
return iScale._reversePixels ? _rlookup(data, axis, value) : _lookup(data, axis, value);
}
return {lo: 0, hi: data.length - 1};
}

/**
Expand All @@ -57,16 +69,11 @@ function useBinarySearch(axis, controller, _sorted, intersect) {
*/
function optimizedEvaluateItems(chart, axis, position, handler, intersect) {
const metasets = chart._getSortedVisibleDatasetMetas();
const value = position[axis];
for (let i = 0, ilen = metasets.length; i < ilen; ++i) {
const {controller, data, index, _sorted} = metasets[i];
let startIndex = 0;
let endIndex = data.length - 1;
if (useBinarySearch(axis, controller, _sorted, intersect)) {
const {loIndex, hiIndex} = _lookup(data, axis, position[axis]);
startIndex = loIndex === null ? hiIndex : loIndex;
endIndex = hiIndex === null ? loIndex : hiIndex;
}
for (let j = startIndex; j <= endIndex; ++j) {
const {index, data} = metasets[i];
let {lo, hi} = binarySearch(metasets[i], axis, value, intersect);
for (let j = lo; j <= hi; ++j) {
const element = data[j];
if (!element.skip) {
handler(element, index, j);
Expand Down
47 changes: 31 additions & 16 deletions src/helpers/helpers.collection.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
'use strict';

/**
* @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
* Binary search
* @param {array} table - the table search. must be sorted!
* @param {string} key - property name for the value in each entry
* @param {number} value - value to find
* @private
*/
export function _lookup(table, key, value) {
let lo = 0;
let hi = table.length - 1;
let mid, i0, i1;
let lo = 0;
let mid;

while (lo >= 0 && lo <= hi) {
while (hi - lo > 1) {
mid = (lo + hi) >> 1;
i0 = mid > 0 && table[mid - 1] || null;
i1 = table[mid];
if (table[mid][key] < value) {
lo = mid;
} else {
hi = mid;
}
}

if (!i0) {
// given value is outside table (before first item)
return {lo: null, hi: i1, loIndex: null, hiIndex: mid};
} else if (i1[key] < value) {
lo = mid + 1;
} else if (i0[key] > value) {
hi = mid - 1;
return {lo, hi};
}

/**
* Reverse binary search
* @param {array} table - the table search. must be sorted!
* @param {string} key - property name for the value in each entry
* @param {number} value - value to find
* @private
*/
export function _rlookup(table, key, value) {
let hi = table.length - 1;
let lo = 0;
let mid;

while (hi - lo > 1) {
mid = (lo + hi) >> 1;
if (table[mid][key] < value) {
hi = mid;
} else {
return {lo: i0, hi: i1, loIndex: mid - 1, hiIndex: mid};
lo = mid;
}
}

// given value is outside table (after last item)
return {lo: i1, hi: null, loIndex: mid, hiIndex: null};
return {lo, hi};
}
6 changes: 3 additions & 3 deletions src/scales/scale.time.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,11 @@ function buildLookupTable(timestamps, min, max, distribution) {
* index [0, 1] or [n - 1, n] are used for the interpolation.
*/
function interpolate(table, skey, sval, tkey) {
const range = _lookup(table, skey, sval);
const {lo, hi} = _lookup(table, skey, sval);

// Note: the lookup table ALWAYS contains at least 2 items (min and max)
const prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
const next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
const prev = table[lo];
const next = table[hi];

const span = next[skey] - prev[skey];
const ratio = span ? (sval - prev[skey]) / span : 0;
Expand Down

0 comments on commit a8cc060

Please sign in to comment.