/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * AUTO-GENERATED FILE. DO NOT MODIFY. */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import quickSelect from './quickSelect.js'; var KDTreeNode = /** @class */function () { function KDTreeNode(axis, data) { this.axis = axis; this.data = data; } return KDTreeNode; }(); /** * @constructor * @alias module:echarts/data/KDTree * @param {Array} points List of points. * each point needs an array property to represent the actual data * @param {Number} [dimension] * Point dimension. * Default will use the first point's length as dimension. */ var KDTree = /** @class */function () { function KDTree(points, dimension) { // Use one stack to avoid allocation // each time searching the nearest point this._stack = []; // Again avoid allocating a new array // each time searching nearest N points this._nearstNList = []; if (!points.length) { return; } if (!dimension) { dimension = points[0].array.length; } this.dimension = dimension; this.root = this._buildTree(points, 0, points.length - 1, 0); } /** * Recursively build the tree. */ KDTree.prototype._buildTree = function (points, left, right, axis) { if (right < left) { return null; } var medianIndex = Math.floor((left + right) / 2); medianIndex = quickSelect(points, left, right, medianIndex, function (a, b) { return a.array[axis] - b.array[axis]; }); var median = points[medianIndex]; var node = new KDTreeNode(axis, median); axis = (axis + 1) % this.dimension; if (right > left) { node.left = this._buildTree(points, left, medianIndex - 1, axis); node.right = this._buildTree(points, medianIndex + 1, right, axis); } return node; }; ; /** * Find nearest point * @param target Target point * @param squaredDistance Squared distance function * @return Nearest point */ KDTree.prototype.nearest = function (target, squaredDistance) { var curr = this.root; var stack = this._stack; var idx = 0; var minDist = Infinity; var nearestNode = null; if (curr.data !== target) { minDist = squaredDistance(curr.data, target); nearestNode = curr; } if (target.array[curr.axis] < curr.data.array[curr.axis]) { // Left first curr.right && (stack[idx++] = curr.right); curr.left && (stack[idx++] = curr.left); } else { // Right first curr.left && (stack[idx++] = curr.left); curr.right && (stack[idx++] = curr.right); } while (idx--) { curr = stack[idx]; var currDist = target.array[curr.axis] - curr.data.array[curr.axis]; var isLeft = currDist < 0; var needsCheckOtherSide = false; currDist = currDist * currDist; // Intersecting right hyperplane with minDist hypersphere if (currDist < minDist) { currDist = squaredDistance(curr.data, target); if (currDist < minDist && curr.data !== target) { minDist = currDist; nearestNode = curr; } needsCheckOtherSide = true; } if (isLeft) { if (needsCheckOtherSide) { curr.right && (stack[idx++] = curr.right); } // Search in the left area curr.left && (stack[idx++] = curr.left); } else { if (needsCheckOtherSide) { curr.left && (stack[idx++] = curr.left); } // Search the right area curr.right && (stack[idx++] = curr.right); } } return nearestNode.data; }; ; KDTree.prototype._addNearest = function (found, dist, node) { var nearestNList = this._nearstNList; var i = found - 1; // Insert to the right position // Sort from small to large for (; i > 0; i--) { if (dist >= nearestNList[i - 1].dist) { break; } else { nearestNList[i].dist = nearestNList[i - 1].dist; nearestNList[i].node = nearestNList[i - 1].node; } } nearestNList[i].dist = dist; nearestNList[i].node = node; }; ; /** * Find nearest N points * @param target Target point * @param N * @param squaredDistance Squared distance function * @param output Output nearest N points */ KDTree.prototype.nearestN = function (target, N, squaredDistance, output) { if (N <= 0) { output.length = 0; return output; } var curr = this.root; var stack = this._stack; var idx = 0; var nearestNList = this._nearstNList; for (var i = 0; i < N; i++) { // Allocate if (!nearestNList[i]) { nearestNList[i] = { dist: 0, node: null }; } nearestNList[i].dist = 0; nearestNList[i].node = null; } var currDist = squaredDistance(curr.data, target); var found = 0; if (curr.data !== target) { found++; this._addNearest(found, currDist, curr); } if (target.array[curr.axis] < curr.data.array[curr.axis]) { // Left first curr.right && (stack[idx++] = curr.right); curr.left && (stack[idx++] = curr.left); } else { // Right first curr.left && (stack[idx++] = curr.left); curr.right && (stack[idx++] = curr.right); } while (idx--) { curr = stack[idx]; var currDist_1 = target.array[curr.axis] - curr.data.array[curr.axis]; var isLeft = currDist_1 < 0; var needsCheckOtherSide = false; currDist_1 = currDist_1 * currDist_1; // Intersecting right hyperplane with minDist hypersphere if (found < N || currDist_1 < nearestNList[found - 1].dist) { currDist_1 = squaredDistance(curr.data, target); if ((found < N || currDist_1 < nearestNList[found - 1].dist) && curr.data !== target) { if (found < N) { found++; } this._addNearest(found, currDist_1, curr); } needsCheckOtherSide = true; } if (isLeft) { if (needsCheckOtherSide) { curr.right && (stack[idx++] = curr.right); } // Search in the left area curr.left && (stack[idx++] = curr.left); } else { if (needsCheckOtherSide) { curr.left && (stack[idx++] = curr.left); } // Search the right area curr.right && (stack[idx++] = curr.right); } } // Copy to output for (var i = 0; i < found; i++) { output[i] = nearestNList[i].node.data; } output.length = found; return output; }; return KDTree; }(); export default KDTree;