|
@@ -18,85 +18,144 @@ THREE.TypedArrayUtils = {};
|
|
|
* orderElement: 0 //order according to x
|
|
|
*/
|
|
|
|
|
|
-THREE.TypedArrayUtils.quicksortIP = function(arr, eleSize, orderElement) {
|
|
|
- var stack = [];
|
|
|
- var sp = -1;
|
|
|
- var left = 0;
|
|
|
- var right = (arr.length) / eleSize - 1;
|
|
|
+THREE.TypedArrayUtils.quicksortIP = function ( arr, eleSize, orderElement ) {
|
|
|
+
|
|
|
+ var stack = [];
|
|
|
+ var sp = -1;
|
|
|
+ var left = 0;
|
|
|
+ var right = arr.length / eleSize - 1;
|
|
|
var tmp = 0.0, x = 0, y = 0;
|
|
|
- var swapF = function(a, b) {
|
|
|
+
|
|
|
+ var swapF = function ( a, b ) {
|
|
|
+
|
|
|
a *= eleSize; b *= eleSize;
|
|
|
- for (y = 0; y < eleSize; y++) {
|
|
|
- tmp=arr[a + y];
|
|
|
- arr[a + y]=arr[b + y];
|
|
|
- arr[b + y]=tmp;
|
|
|
+
|
|
|
+ for ( y = 0; y < eleSize; y ++ ) {
|
|
|
+
|
|
|
+ tmp = arr[ a + y ];
|
|
|
+ arr[ a + y ]=arr[ b + y ];
|
|
|
+ arr[ b + y ]=tmp;
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
};
|
|
|
|
|
|
- var i, j, swap = new Float32Array(eleSize), temp = new Float32Array(eleSize);
|
|
|
- while(true) {
|
|
|
- if(right - left <= 25){
|
|
|
- for(j=left+1; j<=right; j++) {
|
|
|
- for (x = 0; x < eleSize; x++) {
|
|
|
- swap[x] = arr[j * eleSize + x];
|
|
|
+ var i, j, swap = new Float32Array( eleSize ), temp = new Float32Array( eleSize );
|
|
|
+
|
|
|
+ while ( true ) {
|
|
|
+
|
|
|
+ if ( right - left <= 25 ) {
|
|
|
+
|
|
|
+ for ( j= left + 1; j <= right; j ++ ) {
|
|
|
+
|
|
|
+ for ( x = 0; x < eleSize; x ++ ) {
|
|
|
+
|
|
|
+ swap[ x ] = arr[ j * eleSize + x ];
|
|
|
+
|
|
|
}
|
|
|
- i = j-1;
|
|
|
- while(i >= left && arr[i * eleSize + orderElement] > swap[orderElement]) {
|
|
|
- for (x = 0; x < eleSize; x++) {
|
|
|
- arr[(i+1) * eleSize + x] = arr[i * eleSize + x];
|
|
|
+
|
|
|
+ i = j - 1;
|
|
|
+
|
|
|
+ while ( i >= left && arr[ i * eleSize + orderElement ] > swap[orderElement ] ) {
|
|
|
+
|
|
|
+ for ( x = 0; x < eleSize; x ++ ) {
|
|
|
+
|
|
|
+ arr[ ( i + 1 ) * eleSize + x ] = arr[ i * eleSize + x ];
|
|
|
+
|
|
|
}
|
|
|
- i--;
|
|
|
- }
|
|
|
- for (x = 0; x < eleSize; x++) {
|
|
|
- arr[(i+1) * eleSize + x] = swap[x];
|
|
|
+
|
|
|
+ i --;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( x = 0; x < eleSize; x ++ ) {
|
|
|
+
|
|
|
+ arr[ ( i + 1 ) * eleSize + x ] = swap[ x ];
|
|
|
+
|
|
|
}
|
|
|
- }
|
|
|
- if(sp == -1) break;
|
|
|
- right = stack[sp--]; //?
|
|
|
- left = stack[sp--];
|
|
|
- } else {
|
|
|
- var median = (left + right) >> 1;
|
|
|
- i = left + 1;
|
|
|
- j = right;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- swapF(median, i);
|
|
|
- //swap = arr[median]; arr[median] = arr[i]; arr[i] = swap;
|
|
|
- if(arr[left * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
|
|
|
- swapF(left, right);
|
|
|
- //swap = arr[left]; arr[left] = arr[right]; arr[right] = swap;
|
|
|
- } if(arr[i * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
|
|
|
- swapF(i, right);
|
|
|
- //swap = arr[i]; arr[i] = arr[right]; arr[right] = swap;
|
|
|
- } if(arr[left * eleSize + orderElement] > arr[i * eleSize + orderElement]) {
|
|
|
- swapF(left, i);
|
|
|
- //swap = arr[left]; arr[left] = arr[i]; arr[i] = swap;
|
|
|
- }
|
|
|
- for (x = 0; x < eleSize; x++) {
|
|
|
- temp[x] = arr[i * eleSize + x];
|
|
|
+ if ( sp == -1 ) break;
|
|
|
+
|
|
|
+ right = stack[ sp -- ]; //?
|
|
|
+ left = stack[ sp -- ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var median = ( left + right ) >> 1;
|
|
|
+
|
|
|
+ i = left + 1;
|
|
|
+ j = right;
|
|
|
+
|
|
|
+ swapF( median, i );
|
|
|
+ // swap = arr[ median ]; arr[ median ] = arr[ i ]; arr[ i ] = swap;
|
|
|
+
|
|
|
+ if ( arr[ left * eleSize + orderElement ] > arr[ right * eleSize + orderElement ] ) {
|
|
|
+
|
|
|
+ swapF( left, right );
|
|
|
+ // swap = arr[ left ]; arr[ left ] = arr[ right ]; arr[ right ] = swap;
|
|
|
}
|
|
|
- while(true){
|
|
|
- do i++; while(arr[i * eleSize + orderElement] < temp[orderElement]);
|
|
|
- do j--; while(arr[j * eleSize + orderElement] > temp[orderElement]);
|
|
|
- if(j < i) break;
|
|
|
- swapF(i, j);
|
|
|
- //swap = arr[i]; arr[i] = arr[j]; arr[j] = swap;
|
|
|
- }
|
|
|
- for (x = 0; x < eleSize; x++) {
|
|
|
- arr[(left + 1) * eleSize + x] = arr[j * eleSize + x];
|
|
|
- arr[j * eleSize + x] = temp[x];
|
|
|
+
|
|
|
+ if ( arr[ i * eleSize + orderElement ] > arr[ right * eleSize + orderElement ] ) {
|
|
|
+
|
|
|
+ swapF( i, right );
|
|
|
+ // swap = arr[ i ]; arr[ i ] = arr[ right ]; arr[ right ] = swap;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( arr[ left * eleSize + orderElement ] > arr[ i * eleSize + orderElement ] ) {
|
|
|
+
|
|
|
+ swapF( left, i );
|
|
|
+ // swap = arr[ left ]; arr[ left ] = arr[ i ]; arr[ i ] = swap;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( x = 0; x < eleSize; x ++ ) {
|
|
|
+
|
|
|
+ temp[ x ] = arr[ i * eleSize + x ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ while ( true ) {
|
|
|
+
|
|
|
+ do i ++; while ( arr[ i * eleSize + orderElement ] < temp[ orderElement ] );
|
|
|
+ do j --; while ( arr[ j * eleSize + orderElement ] > temp[ orderElement ] );
|
|
|
+
|
|
|
+ if ( j < i ) break;
|
|
|
+
|
|
|
+ swapF( i, j );
|
|
|
+ // swap = arr[ i ]; arr[ i ] = arr[ j ]; arr[ j ] = swap;
|
|
|
+
|
|
|
}
|
|
|
- if(right - i + 1 >= j - left){
|
|
|
- stack[++sp] = i;
|
|
|
- stack[++sp] = right;
|
|
|
- right = j - 1;
|
|
|
- }else{
|
|
|
- stack[++sp] = left;
|
|
|
- stack[++sp] = j - 1;
|
|
|
- left = i;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return arr;
|
|
|
+
|
|
|
+ for ( x = 0; x < eleSize; x ++ ) {
|
|
|
+
|
|
|
+ arr[ ( left + 1 ) * eleSize + x ] = arr[ j * eleSize + x ];
|
|
|
+ arr[ j * eleSize + x ] = temp[ x ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( right - i + 1 >= j - left ) {
|
|
|
+
|
|
|
+ stack[ ++ sp ] = i;
|
|
|
+ stack[ ++ sp ] = right;
|
|
|
+ right = j - 1;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ stack[ ++ sp ] = left;
|
|
|
+ stack[ ++ sp ] = j - 1;
|
|
|
+ left = i;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return arr;
|
|
|
+
|
|
|
};
|
|
|
|
|
|
|
|
@@ -127,277 +186,398 @@ THREE.TypedArrayUtils.quicksortIP = function(arr, eleSize, orderElement) {
|
|
|
* If you want to further minimize performance, remove Node.depth and replace in search algorithm with a traversal to root node
|
|
|
*/
|
|
|
|
|
|
- THREE.TypedArrayUtils.Kdtree = function (points, metric, eleSize) {
|
|
|
+ THREE.TypedArrayUtils.Kdtree = function ( points, metric, eleSize ) {
|
|
|
|
|
|
- var self = this;
|
|
|
+ var self = this;
|
|
|
|
|
|
var maxDepth = 0;
|
|
|
|
|
|
- var getPointSet = function(points, pos) {
|
|
|
- return points.subarray(pos * eleSize, pos * eleSize + eleSize);
|
|
|
+ var getPointSet = function ( points, pos ) {
|
|
|
+
|
|
|
+ return points.subarray( pos * eleSize, pos * eleSize + eleSize );
|
|
|
+
|
|
|
};
|
|
|
-
|
|
|
- function buildTree(points, depth, parent, pos) {
|
|
|
- var dim = depth % eleSize,
|
|
|
- median,
|
|
|
- node,
|
|
|
- plength = points.length / eleSize;
|
|
|
-
|
|
|
- if (depth > maxDepth) maxDepth = depth;
|
|
|
|
|
|
- if (plength === 0) return null;
|
|
|
- if (plength === 1) {
|
|
|
- return new self.Node(getPointSet(points, 0), depth, parent, pos);
|
|
|
- }
|
|
|
-
|
|
|
- THREE.TypedArrayUtils.quicksortIP(points, eleSize, dim);
|
|
|
-
|
|
|
- median = Math.floor(plength / 2);
|
|
|
-
|
|
|
- //debugger;
|
|
|
- node = new self.Node(getPointSet(points, median) , depth, parent, median + pos);
|
|
|
- node.left = buildTree(points.subarray( 0, median * eleSize), depth + 1, node, pos);
|
|
|
- node.right = buildTree(points.subarray( (median + 1) * eleSize, points.length), depth + 1, node, pos + median + 1);
|
|
|
-
|
|
|
- return node;
|
|
|
- }
|
|
|
-
|
|
|
- this.root = buildTree(points, 0, null, 0);
|
|
|
+ function buildTree( points, depth, parent, pos ) {
|
|
|
+
|
|
|
+ var dim = depth % eleSize,
|
|
|
+ median,
|
|
|
+ node,
|
|
|
+ plength = points.length / eleSize;
|
|
|
+
|
|
|
+ if ( depth > maxDepth ) maxDepth = depth;
|
|
|
+
|
|
|
+ if ( plength === 0 ) return null;
|
|
|
+ if ( plength === 1 ) {
|
|
|
+
|
|
|
+ return new self.Node( getPointSet( points, 0 ), depth, parent, pos );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ THREE.TypedArrayUtils.quicksortIP( points, eleSize, dim );
|
|
|
+
|
|
|
+ median = Math.floor( plength / 2 );
|
|
|
|
|
|
- this.getMaxDepth = function() { return maxDepth; };
|
|
|
+ // debugger;
|
|
|
+ node = new self.Node( getPointSet( points, median ) , depth, parent, median + pos );
|
|
|
+ node.left = buildTree( points.subarray( 0, median * eleSize), depth + 1, node, pos );
|
|
|
+ node.right = buildTree( points.subarray( ( median + 1 ) * eleSize, points.length ), depth + 1, node, pos + median + 1 );
|
|
|
+
|
|
|
+ return node;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.root = buildTree( points, 0, null, 0 );
|
|
|
+
|
|
|
+ this.getMaxDepth = function () { return maxDepth; };
|
|
|
|
|
|
/* point: array of size eleSize
|
|
|
maxNodes: max amount of nodes to return
|
|
|
- maxDistance: maximum distance to point result nodes should have */
|
|
|
- this.nearest = function (point, maxNodes , maxDistance ) {
|
|
|
- var i,
|
|
|
- result,
|
|
|
- bestNodes;
|
|
|
-
|
|
|
- bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap(
|
|
|
- function (e) { return -e[1]; }
|
|
|
- );
|
|
|
-
|
|
|
- function nearestSearch(node) {
|
|
|
- var bestChild,
|
|
|
- dimension = node.depth % eleSize,
|
|
|
- ownDistance = metric(point, node.obj),
|
|
|
- linearDistance = 0,
|
|
|
- otherChild,
|
|
|
- i,
|
|
|
- linearPoint = [];
|
|
|
-
|
|
|
- function saveNode(node, distance) {
|
|
|
- bestNodes.push([node, distance]);
|
|
|
- if (bestNodes.size() > maxNodes) {
|
|
|
- bestNodes.pop();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < eleSize; i += 1) {
|
|
|
- if (i === node.depth % eleSize) {
|
|
|
- linearPoint[i] = point[i];
|
|
|
- } else {
|
|
|
- linearPoint[i] = node.obj[i];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- linearDistance = metric(linearPoint, node.obj);
|
|
|
-
|
|
|
- //if it's a leaf
|
|
|
- if (node.right === null && node.left === null) {
|
|
|
- if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
|
|
|
- saveNode(node, ownDistance);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (node.right === null) {
|
|
|
- bestChild = node.left;
|
|
|
- } else if (node.left === null) {
|
|
|
- bestChild = node.right;
|
|
|
- } else {
|
|
|
- if (point[dimension] < node.obj[dimension]) {
|
|
|
- bestChild = node.left;
|
|
|
- } else {
|
|
|
- bestChild = node.right;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //recursive search
|
|
|
- nearestSearch(bestChild);
|
|
|
-
|
|
|
- if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
|
|
|
- saveNode(node, ownDistance);
|
|
|
- }
|
|
|
-
|
|
|
- //if there's still room or the current distance is nearer than the best distance
|
|
|
- if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
|
|
|
- if (bestChild === node.left) {
|
|
|
- otherChild = node.right;
|
|
|
- } else {
|
|
|
- otherChild = node.left;
|
|
|
- }
|
|
|
- if (otherChild !== null) {
|
|
|
- nearestSearch(otherChild);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (maxDistance) {
|
|
|
- for (i = 0; i < maxNodes; i += 1) {
|
|
|
- bestNodes.push([null, maxDistance]);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- nearestSearch(self.root);
|
|
|
-
|
|
|
- result = [];
|
|
|
-
|
|
|
- for (i = 0; i < maxNodes; i += 1) {
|
|
|
- if (bestNodes.content[i][0]) {
|
|
|
- result.push([bestNodes.content[i][0], bestNodes.content[i][1]]);
|
|
|
- }
|
|
|
- }
|
|
|
- return result;
|
|
|
- };
|
|
|
+ maxDistance: maximum distance to point result nodes should have
|
|
|
+ */
|
|
|
+
|
|
|
+ this.nearest = function ( point, maxNodes , maxDistance ) {
|
|
|
+
|
|
|
+ var i,
|
|
|
+ result,
|
|
|
+ bestNodes;
|
|
|
+
|
|
|
+ bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap(
|
|
|
+
|
|
|
+ function ( e ) { return -e[ 1 ]; }
|
|
|
+
|
|
|
+ );
|
|
|
+
|
|
|
+ function nearestSearch( node ) {
|
|
|
+
|
|
|
+ var bestChild,
|
|
|
+ dimension = node.depth % eleSize,
|
|
|
+ ownDistance = metric(point, node.obj),
|
|
|
+ linearDistance = 0,
|
|
|
+ otherChild,
|
|
|
+ i,
|
|
|
+ linearPoint = [];
|
|
|
+
|
|
|
+ function saveNode( node, distance ) {
|
|
|
+
|
|
|
+ bestNodes.push( [ node, distance ] );
|
|
|
+
|
|
|
+ if ( bestNodes.size() > maxNodes ) {
|
|
|
+
|
|
|
+ bestNodes.pop();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( i = 0; i < eleSize; i += 1 ) {
|
|
|
+
|
|
|
+ if ( i === node.depth % eleSize ) {
|
|
|
+
|
|
|
+ linearPoint[ i ] = point[ i ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ linearPoint[ i ] = node.obj[ i ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ linearDistance = metric( linearPoint, node.obj );
|
|
|
+
|
|
|
+ // if it's a leaf
|
|
|
+
|
|
|
+ if ( node.right === null && node.left === null ) {
|
|
|
+
|
|
|
+ if ( bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ] ) {
|
|
|
+
|
|
|
+ saveNode( node, ownDistance );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( node.right === null ) {
|
|
|
+
|
|
|
+ bestChild = node.left;
|
|
|
+
|
|
|
+ } else if ( node.left === null ) {
|
|
|
+
|
|
|
+ bestChild = node.right;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if ( point[ dimension ] < node.obj[ dimension ] ) {
|
|
|
+
|
|
|
+ bestChild = node.left;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ bestChild = node.right;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // recursive search
|
|
|
+
|
|
|
+ nearestSearch( bestChild );
|
|
|
+
|
|
|
+ if ( bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ] ) {
|
|
|
+
|
|
|
+ saveNode( node, ownDistance );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // if there's still room or the current distance is nearer than the best distance
|
|
|
+
|
|
|
+ if ( bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[ 1 ] ) {
|
|
|
+
|
|
|
+ if ( bestChild === node.left ) {
|
|
|
+
|
|
|
+ otherChild = node.right;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ otherChild = node.left;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( otherChild !== null ) {
|
|
|
+
|
|
|
+ nearestSearch( otherChild );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( maxDistance ) {
|
|
|
+
|
|
|
+ for ( i = 0; i < maxNodes; i += 1 ) {
|
|
|
+
|
|
|
+ bestNodes.push( [ null, maxDistance ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ nearestSearch( self.root );
|
|
|
+
|
|
|
+ result = [];
|
|
|
+
|
|
|
+ for ( i = 0; i < maxNodes; i += 1 ) {
|
|
|
+
|
|
|
+ if ( bestNodes.content[ i ][ 0 ] ) {
|
|
|
+
|
|
|
+ result.push( [ bestNodes.content[ i ][ 0 ], bestNodes.content[ i ][ 1 ] ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- THREE.TypedArrayUtils.Kdtree.prototype.Node = function(obj, depth, parent, pos) {
|
|
|
- this.obj = obj;
|
|
|
- this.left = null;
|
|
|
- this.right = null;
|
|
|
- this.parent = parent;
|
|
|
- this.depth = depth;
|
|
|
+THREE.TypedArrayUtils.Kdtree.prototype.Node = function ( obj, depth, parent, pos ) {
|
|
|
+
|
|
|
+ this.obj = obj;
|
|
|
+ this.left = null;
|
|
|
+ this.right = null;
|
|
|
+ this.parent = parent;
|
|
|
+ this.depth = depth;
|
|
|
this.pos = pos;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* Binary heap implementation
|
|
|
* @author http://eloquentjavascript.net/appendix2.htm
|
|
|
*/
|
|
|
- THREE.TypedArrayUtils.Kdtree.BinaryHeap = function(scoreFunction){
|
|
|
- this.content = [];
|
|
|
- this.scoreFunction = scoreFunction;
|
|
|
- }
|
|
|
-
|
|
|
- THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = {
|
|
|
- push: function(element) {
|
|
|
- // Add the new element to the end of the array.
|
|
|
- this.content.push(element);
|
|
|
- // Allow it to bubble up.
|
|
|
- this.bubbleUp(this.content.length - 1);
|
|
|
- },
|
|
|
-
|
|
|
- pop: function() {
|
|
|
- // Store the first element so we can return it later.
|
|
|
- var result = this.content[0];
|
|
|
- // Get the element at the end of the array.
|
|
|
- var end = this.content.pop();
|
|
|
- // If there are any elements left, put the end element at the
|
|
|
- // start, and let it sink down.
|
|
|
- if (this.content.length > 0) {
|
|
|
- this.content[0] = end;
|
|
|
- this.sinkDown(0);
|
|
|
- }
|
|
|
- return result;
|
|
|
- },
|
|
|
-
|
|
|
- peek: function() {
|
|
|
- return this.content[0];
|
|
|
- },
|
|
|
-
|
|
|
- remove: function(node) {
|
|
|
- var len = this.content.length;
|
|
|
- // To remove a value, we must search through the array to find
|
|
|
- // it.
|
|
|
- for (var i = 0; i < len; i++) {
|
|
|
- if (this.content[i] == node) {
|
|
|
- // When it is found, the process seen in 'pop' is repeated
|
|
|
- // to fill up the hole.
|
|
|
- var end = this.content.pop();
|
|
|
- if (i != len - 1) {
|
|
|
- this.content[i] = end;
|
|
|
- if (this.scoreFunction(end) < this.scoreFunction(node))
|
|
|
- this.bubbleUp(i);
|
|
|
- else
|
|
|
- this.sinkDown(i);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- throw new Error("Node not found.");
|
|
|
- },
|
|
|
-
|
|
|
- size: function() {
|
|
|
- return this.content.length;
|
|
|
- },
|
|
|
-
|
|
|
- bubbleUp: function(n) {
|
|
|
- // Fetch the element that has to be moved.
|
|
|
- var element = this.content[n];
|
|
|
- // When at 0, an element can not go up any further.
|
|
|
- while (n > 0) {
|
|
|
- // Compute the parent element's index, and fetch it.
|
|
|
- var parentN = Math.floor((n + 1) / 2) - 1,
|
|
|
- parent = this.content[parentN];
|
|
|
- // Swap the elements if the parent is greater.
|
|
|
- if (this.scoreFunction(element) < this.scoreFunction(parent)) {
|
|
|
- this.content[parentN] = element;
|
|
|
- this.content[n] = parent;
|
|
|
- // Update 'n' to continue at the new position.
|
|
|
- n = parentN;
|
|
|
- }
|
|
|
- // Found a parent that is less, no need to move it further.
|
|
|
- else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- sinkDown: function(n) {
|
|
|
- // Look up the target element and its score.
|
|
|
- var length = this.content.length,
|
|
|
- element = this.content[n],
|
|
|
- elemScore = this.scoreFunction(element);
|
|
|
-
|
|
|
- while(true) {
|
|
|
- // Compute the indices of the child elements.
|
|
|
- var child2N = (n + 1) * 2, child1N = child2N - 1;
|
|
|
- // This is used to store the new position of the element,
|
|
|
- // if any.
|
|
|
- var swap = null;
|
|
|
- // If the first child exists (is inside the array)...
|
|
|
- if (child1N < length) {
|
|
|
- // Look it up and compute its score.
|
|
|
- var child1 = this.content[child1N],
|
|
|
- child1Score = this.scoreFunction(child1);
|
|
|
- // If the score is less than our element's, we need to swap.
|
|
|
- if (child1Score < elemScore)
|
|
|
- swap = child1N;
|
|
|
- }
|
|
|
- // Do the same checks for the other child.
|
|
|
- if (child2N < length) {
|
|
|
- var child2 = this.content[child2N],
|
|
|
- child2Score = this.scoreFunction(child2);
|
|
|
- if (child2Score < (swap === null ? elemScore : child1Score)){
|
|
|
- swap = child2N;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // If the element needs to be moved, swap it, and continue.
|
|
|
- if (swap !== null) {
|
|
|
- this.content[n] = this.content[swap];
|
|
|
- this.content[swap] = element;
|
|
|
- n = swap;
|
|
|
- }
|
|
|
- // Otherwise, we are done.
|
|
|
- else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
+
|
|
|
+THREE.TypedArrayUtils.Kdtree.BinaryHeap = function ( scoreFunction ) {
|
|
|
+
|
|
|
+ this.content = [];
|
|
|
+ this.scoreFunction = scoreFunction;
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = {
|
|
|
+
|
|
|
+ push: function ( element ) {
|
|
|
+
|
|
|
+ // Add the new element to the end of the array.
|
|
|
+ this.content.push( element );
|
|
|
+
|
|
|
+ // Allow it to bubble up.
|
|
|
+ this.bubbleUp( this.content.length - 1 );
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ pop: function () {
|
|
|
+
|
|
|
+ // Store the first element so we can return it later.
|
|
|
+ var result = this.content[ 0 ];
|
|
|
+
|
|
|
+ // Get the element at the end of the array.
|
|
|
+ var end = this.content.pop();
|
|
|
+
|
|
|
+ // If there are any elements left, put the end element at the
|
|
|
+ // start, and let it sink down.
|
|
|
+ if ( this.content.length > 0 ) {
|
|
|
+
|
|
|
+ this.content[ 0 ] = end;
|
|
|
+ this.sinkDown( 0 );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ peek: function () {
|
|
|
+
|
|
|
+ return this.content[ 0 ];
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ remove: function ( node ) {
|
|
|
+
|
|
|
+ var len = this.content.length;
|
|
|
+
|
|
|
+ // To remove a value, we must search through the array to find it.
|
|
|
+ for ( var i = 0; i < len; i ++ ) {
|
|
|
+
|
|
|
+ if ( this.content[ i ] == node ) {
|
|
|
+
|
|
|
+ // When it is found, the process seen in 'pop' is repeated
|
|
|
+ // to fill up the hole.
|
|
|
+ var end = this.content.pop();
|
|
|
+
|
|
|
+ if ( i != len - 1 ) {
|
|
|
+
|
|
|
+ this.content[ i ] = end;
|
|
|
+
|
|
|
+ if ( this.scoreFunction( end ) < this.scoreFunction( node ) ) {
|
|
|
+
|
|
|
+ this.bubbleUp( i );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ this.sinkDown( i );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new Error( "Node not found." );
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ size: function () {
|
|
|
+
|
|
|
+ return this.content.length;
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ bubbleUp: function ( n ) {
|
|
|
+
|
|
|
+ // Fetch the element that has to be moved.
|
|
|
+ var element = this.content[ n ];
|
|
|
+
|
|
|
+ // When at 0, an element can not go up any further.
|
|
|
+ while ( n > 0 ) {
|
|
|
+
|
|
|
+ // Compute the parent element's index, and fetch it.
|
|
|
+ var parentN = Math.floor( ( n + 1 ) / 2 ) - 1,
|
|
|
+ parent = this.content[ parentN ];
|
|
|
+
|
|
|
+ // Swap the elements if the parent is greater.
|
|
|
+ if ( this.scoreFunction( element ) < this.scoreFunction( parent ) ) {
|
|
|
+
|
|
|
+ this.content[ parentN ] = element;
|
|
|
+ this.content[ n ] = parent;
|
|
|
+
|
|
|
+ // Update 'n' to continue at the new position.
|
|
|
+ n = parentN;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // Found a parent that is less, no need to move it further.
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ sinkDown: function ( n ) {
|
|
|
+
|
|
|
+ // Look up the target element and its score.
|
|
|
+ var length = this.content.length,
|
|
|
+ element = this.content[ n ],
|
|
|
+ elemScore = this.scoreFunction( element );
|
|
|
+
|
|
|
+ while ( true ) {
|
|
|
+
|
|
|
+ // Compute the indices of the child elements.
|
|
|
+ var child2N = ( n + 1 ) * 2, child1N = child2N - 1;
|
|
|
+
|
|
|
+ // This is used to store the new position of the element, if any.
|
|
|
+ var swap = null;
|
|
|
+
|
|
|
+ // If the first child exists (is inside the array)...
|
|
|
+ if ( child1N < length ) {
|
|
|
+
|
|
|
+ // Look it up and compute its score.
|
|
|
+ var child1 = this.content[ child1N ],
|
|
|
+ child1Score = this.scoreFunction( child1 );
|
|
|
+
|
|
|
+ // If the score is less than our element's, we need to swap.
|
|
|
+ if ( child1Score < elemScore ) swap = child1N;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Do the same checks for the other child.
|
|
|
+ if ( child2N < length ) {
|
|
|
+
|
|
|
+ var child2 = this.content[ child2N ],
|
|
|
+ child2Score = this.scoreFunction( child2 );
|
|
|
+
|
|
|
+ if ( child2Score < ( swap === null ? elemScore : child1Score ) ) swap = child2N;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the element needs to be moved, swap it, and continue.
|
|
|
+ if ( swap !== null ) {
|
|
|
+
|
|
|
+ this.content[ n ] = this.content[ swap ];
|
|
|
+ this.content[ swap ] = element;
|
|
|
+ n = swap;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // Otherwise, we are done.
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|