UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

141 lines 4.36 kB
import FlatBush from "flatbush"; import { Indices } from "../types"; import { empty } from "./bbox"; function upperBound(value, arr) { let i = 0; let j = arr.length - 1; while (i < j) { const m = (i + j) >> 1; if (arr[m] > value) { j = m; } else { i = m + 1; } } return arr[i]; } class _FlatBush extends FlatBush { static __name__ = "_FlatBush"; search_apply(minX, minY, maxX, maxY, nodeFunction) { if (this._pos !== this._boxes.length) { throw new Error("Data not yet indexed - call index.finish()."); } let nodeIndex = this._boxes.length - 4; const queue = []; while (nodeIndex !== undefined) { // find the end index of the node const end = Math.min(nodeIndex + this.nodeSize * 4, upperBound(nodeIndex, this._levelBounds)); // search through child nodes for (let pos = nodeIndex; pos < end; pos += 4) { const nodeMinX = this._boxes[pos + 0]; const nodeMinY = this._boxes[pos + 1]; const nodeMaxX = this._boxes[pos + 2]; const nodeMaxY = this._boxes[pos + 3]; if (maxX < nodeMinX || maxY < nodeMinY || minX > nodeMaxX || minY > nodeMaxY) { continue; } const index = this._indices[pos >> 2] | 0; if (nodeIndex < this.numItems * 4) { nodeFunction(index, { x0: nodeMinX, y0: nodeMinY, x1: nodeMaxX, y1: nodeMaxY }); } else { queue.push(index); // node; add it to the search queue } } nodeIndex = queue.pop(); } } search_indices(minX, minY, maxX, maxY) { const result = new Indices(this.numItems); this.search_apply(minX, minY, maxX, maxY, (index) => result.set(index)); return result; } search_bounds(minX, minY, maxX, maxY) { const result = empty(); this.search_apply(minX, minY, maxX, maxY, (_index, node) => { if (node.x0 >= minX && node.x0 < result.x0) { result.x0 = node.x0; } if (node.x1 <= maxX && node.x1 > result.x1) { result.x1 = node.x1; } if (node.y0 >= minY && node.y0 < result.y0) { result.y0 = node.y0; } if (node.y1 <= maxY && node.y1 > result.y1) { result.y1 = node.y1; } }); return result; } } export class SpatialIndex { static __name__ = "SpatialIndex"; index = null; constructor(size) { if (size > 0) { this.index = new _FlatBush(size); } } add_rect(x0, y0, x1, y1) { if (!isFinite(x0 + y0 + x1 + y1)) { this.add_empty(); } else { this.index?.add(x0, y0, x1, y1); } } add_point(x, y) { if (!isFinite(x + y)) { this.add_empty(); } else { this.index?.add(x, y, x, y); } } add_empty() { this.index?.add(Infinity, Infinity, -Infinity, -Infinity); } finish() { this.index?.finish(); } _normalize(rect) { let { x0, y0, x1, y1 } = rect; if ((x0 > x1) && isFinite(x0 + x1)) { [x0, x1] = [x1, x0]; } if ((y0 > y1) && isFinite(y0 + y1)) { [y0, y1] = [y1, y0]; } return { x0, y0, x1, y1 }; } get bbox() { if (this.index == null) { return empty(); } else { const { minX, minY, maxX, maxY } = this.index; return { x0: minX, y0: minY, x1: maxX, y1: maxY }; } } indices(rect) { if (this.index == null) { return new Indices(0); } else { const { x0, y0, x1, y1 } = this._normalize(rect); return this.index.search_indices(x0, y0, x1, y1); } } bounds(rect) { if (this.index == null) { return empty(); } else { const { x0, y0, x1, y1 } = this._normalize(rect); return this.index.search_bounds(x0, y0, x1, y1); } } } //# sourceMappingURL=spatial.js.map