123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- THREE.TypedArrayUtils = {};
- /**
- * In-place quicksort for typed arrays (e.g. for Float32Array)
- * provides fast sorting
- * useful e.g. for a custom shader and/or BufferGeometry
- *
- * @author Roman Bolzern <[email protected]>, 2013
- * @author I4DS http://www.fhnw.ch/i4ds, 2013
- * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
- *
- * Complexity: http://bigocheatsheet.com/ see Quicksort
- *
- * Example:
- * points: [x, y, z, x, y, z, x, y, z, ...]
- * eleSize: 3 //because of (x, y, z)
- * 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;
- var tmp = 0.0, x = 0, y = 0;
- 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;
- }
- };
-
- 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--;
- }
- 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];
- }
- 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(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;
- };
- /**
- * k-d Tree for typed arrays (e.g. for Float32Array), in-place
- * provides fast nearest neighbour search
- * useful e.g. for a custom shader and/or BufferGeometry, saves tons of memory
- * has no insert and remove, only buildup and neares neighbour search
- *
- * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs
- *
- * @author Roman Bolzern <[email protected]>, 2013
- * @author I4DS http://www.fhnw.ch/i4ds, 2013
- * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
- *
- * Requires typed array quicksort
- *
- * Example:
- * points: [x, y, z, x, y, z, x, y, z, ...]
- * metric: function(a, b){ return Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2); } //Manhatten distance
- * eleSize: 3 //because of (x, y, z)
- *
- * Further information (including mathematical properties)
- * http://en.wikipedia.org/wiki/Binary_tree
- * http://en.wikipedia.org/wiki/K-d_tree
- *
- * 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) {
- var self = this;
-
- var maxDepth = 0;
-
- 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);
-
- 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;
- };
-
- }
-
- 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;
- }
- }
- }
- };
|