| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 | 
/** 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.*/var quickSelect = require("./quickSelect");/** 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.*/function Node(axis, data) {  this.left = null;  this.right = null;  this.axis = axis;  this.data = data;}/** * @constructor * @alias module:echarts/data/KDTree * @param {Array} points List of points. * each point needs an array property to repesent the actual data * @param {Number} [dimension] *        Point dimension. *        Default will use the first point's length as dimensiont */var KDTree = function (points, dimension) {  if (!points.length) {    return;  }  if (!dimension) {    dimension = points[0].array.length;  }  this.dimension = dimension;  this.root = this._buildTree(points, 0, points.length - 1, 0); // 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 = [];};/** * Resursively 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 Node(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  {Array} target Target point * @param  {Function} squaredDistance Squared distance function * @return {Array} 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; // Insert to the right position  // Sort from small to large  for (var i = found - 1; 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  {Array} target Target point * @param  {number} N * @param  {Function} squaredDistance Squared distance function * @param  {Array} [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] = {};    }    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 = 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 (found < N || currDist < nearestNList[found - 1].dist) {      currDist = squaredDistance(curr.data, target);      if ((found < N || currDist < nearestNList[found - 1].dist) && curr.data !== target) {        if (found < N) {          found++;        }        this._addNearest(found, currDist, 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;};var _default = KDTree;module.exports = _default;
 |