TypedArrayUtils.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. THREE.TypedArrayUtils = {};
  2. /**
  3. * In-place quicksort for typed arrays (e.g. for Float32Array)
  4. * provides fast sorting
  5. * useful e.g. for a custom shader and/or BufferGeometry
  6. *
  7. * @author Roman Bolzern <[email protected]>, 2013
  8. * @author I4DS http://www.fhnw.ch/i4ds, 2013
  9. * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
  10. *
  11. * Complexity: http://bigocheatsheet.com/ see Quicksort
  12. *
  13. * Example:
  14. * points: [x, y, z, x, y, z, x, y, z, ...]
  15. * eleSize: 3 //because of (x, y, z)
  16. * orderElement: 0 //order according to x
  17. */
  18. THREE.TypedArrayUtils.quicksortIP = function(arr, eleSize, orderElement) {
  19. var stack = [];
  20. var sp = -1;
  21. var left = 0;
  22. var right = (arr.length) / eleSize - 1;
  23. var tmp = 0.0, x = 0, y = 0;
  24. var swapF = function(a, b) {
  25. a *= eleSize; b *= eleSize;
  26. for (y = 0; y < eleSize; y++) {
  27. tmp=arr[a + y];
  28. arr[a + y]=arr[b + y];
  29. arr[b + y]=tmp;
  30. }
  31. };
  32. var i, j, swap = new Float32Array(eleSize), temp = new Float32Array(eleSize);
  33. while(true) {
  34. if(right - left <= 25){
  35. for(j=left+1; j<=right; j++) {
  36. for (x = 0; x < eleSize; x++) {
  37. swap[x] = arr[j * eleSize + x];
  38. }
  39. i = j-1;
  40. while(i >= left && arr[i * eleSize + orderElement] > swap[orderElement]) {
  41. for (x = 0; x < eleSize; x++) {
  42. arr[(i+1) * eleSize + x] = arr[i * eleSize + x];
  43. }
  44. i--;
  45. }
  46. for (x = 0; x < eleSize; x++) {
  47. arr[(i+1) * eleSize + x] = swap[x];
  48. }
  49. }
  50. if(sp == -1) break;
  51. right = stack[sp--]; //?
  52. left = stack[sp--];
  53. } else {
  54. var median = (left + right) >> 1;
  55. i = left + 1;
  56. j = right;
  57. swapF(median, i);
  58. //swap = arr[median]; arr[median] = arr[i]; arr[i] = swap;
  59. if(arr[left * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
  60. swapF(left, right);
  61. //swap = arr[left]; arr[left] = arr[right]; arr[right] = swap;
  62. } if(arr[i * eleSize + orderElement] > arr[right * eleSize + orderElement]) {
  63. swapF(i, right);
  64. //swap = arr[i]; arr[i] = arr[right]; arr[right] = swap;
  65. } if(arr[left * eleSize + orderElement] > arr[i * eleSize + orderElement]) {
  66. swapF(left, i);
  67. //swap = arr[left]; arr[left] = arr[i]; arr[i] = swap;
  68. }
  69. for (x = 0; x < eleSize; x++) {
  70. temp[x] = arr[i * eleSize + x];
  71. }
  72. while(true){
  73. do i++; while(arr[i * eleSize + orderElement] < temp[orderElement]);
  74. do j--; while(arr[j * eleSize + orderElement] > temp[orderElement]);
  75. if(j < i) break;
  76. swapF(i, j);
  77. //swap = arr[i]; arr[i] = arr[j]; arr[j] = swap;
  78. }
  79. for (x = 0; x < eleSize; x++) {
  80. arr[(left + 1) * eleSize + x] = arr[j * eleSize + x];
  81. arr[j * eleSize + x] = temp[x];
  82. }
  83. if(right - i + 1 >= j - left){
  84. stack[++sp] = i;
  85. stack[++sp] = right;
  86. right = j - 1;
  87. }else{
  88. stack[++sp] = left;
  89. stack[++sp] = j - 1;
  90. left = i;
  91. }
  92. }
  93. }
  94. return arr;
  95. };
  96. /**
  97. * k-d Tree for typed arrays (e.g. for Float32Array), in-place
  98. * provides fast nearest neighbour search
  99. * useful e.g. for a custom shader and/or BufferGeometry, saves tons of memory
  100. * has no insert and remove, only buildup and neares neighbour search
  101. *
  102. * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs
  103. *
  104. * @author Roman Bolzern <[email protected]>, 2013
  105. * @author I4DS http://www.fhnw.ch/i4ds, 2013
  106. * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
  107. *
  108. * Requires typed array quicksort
  109. *
  110. * Example:
  111. * points: [x, y, z, x, y, z, x, y, z, ...]
  112. * 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
  113. * eleSize: 3 //because of (x, y, z)
  114. *
  115. * Further information (including mathematical properties)
  116. * http://en.wikipedia.org/wiki/Binary_tree
  117. * http://en.wikipedia.org/wiki/K-d_tree
  118. *
  119. * If you want to further minimize performance, remove Node.depth and replace in search algorithm with a traversal to root node
  120. */
  121. THREE.TypedArrayUtils.Kdtree = function (points, metric, eleSize) {
  122. var self = this;
  123. var maxDepth = 0;
  124. var getPointSet = function(points, pos) {
  125. return points.subarray(pos * eleSize, pos * eleSize + eleSize);
  126. };
  127. function buildTree(points, depth, parent, pos) {
  128. var dim = depth % eleSize,
  129. median,
  130. node,
  131. plength = points.length / eleSize;
  132. if (depth > maxDepth) maxDepth = depth;
  133. if (plength === 0) return null;
  134. if (plength === 1) {
  135. return new self.Node(getPointSet(points, 0), depth, parent, pos);
  136. }
  137. THREE.TypedArrayUtils.quicksortIP(points, eleSize, dim);
  138. median = Math.floor(plength / 2);
  139. //debugger;
  140. node = new self.Node(getPointSet(points, median) , depth, parent, median + pos);
  141. node.left = buildTree(points.subarray( 0, median * eleSize), depth + 1, node, pos);
  142. node.right = buildTree(points.subarray( (median + 1) * eleSize, points.length), depth + 1, node, pos + median + 1);
  143. return node;
  144. }
  145. this.root = buildTree(points, 0, null, 0);
  146. this.getMaxDepth = function() { return maxDepth; };
  147. /* point: array of size eleSize
  148. maxNodes: max amount of nodes to return
  149. maxDistance: maximum distance to point result nodes should have */
  150. this.nearest = function (point, maxNodes , maxDistance ) {
  151. var i,
  152. result,
  153. bestNodes;
  154. bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap(
  155. function (e) { return -e[1]; }
  156. );
  157. function nearestSearch(node) {
  158. var bestChild,
  159. dimension = node.depth % eleSize,
  160. ownDistance = metric(point, node.obj),
  161. linearDistance = 0,
  162. otherChild,
  163. i,
  164. linearPoint = [];
  165. function saveNode(node, distance) {
  166. bestNodes.push([node, distance]);
  167. if (bestNodes.size() > maxNodes) {
  168. bestNodes.pop();
  169. }
  170. }
  171. for (i = 0; i < eleSize; i += 1) {
  172. if (i === node.depth % eleSize) {
  173. linearPoint[i] = point[i];
  174. } else {
  175. linearPoint[i] = node.obj[i];
  176. }
  177. }
  178. linearDistance = metric(linearPoint, node.obj);
  179. //if it's a leaf
  180. if (node.right === null && node.left === null) {
  181. if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
  182. saveNode(node, ownDistance);
  183. }
  184. return;
  185. }
  186. if (node.right === null) {
  187. bestChild = node.left;
  188. } else if (node.left === null) {
  189. bestChild = node.right;
  190. } else {
  191. if (point[dimension] < node.obj[dimension]) {
  192. bestChild = node.left;
  193. } else {
  194. bestChild = node.right;
  195. }
  196. }
  197. //recursive search
  198. nearestSearch(bestChild);
  199. if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
  200. saveNode(node, ownDistance);
  201. }
  202. //if there's still room or the current distance is nearer than the best distance
  203. if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
  204. if (bestChild === node.left) {
  205. otherChild = node.right;
  206. } else {
  207. otherChild = node.left;
  208. }
  209. if (otherChild !== null) {
  210. nearestSearch(otherChild);
  211. }
  212. }
  213. }
  214. if (maxDistance) {
  215. for (i = 0; i < maxNodes; i += 1) {
  216. bestNodes.push([null, maxDistance]);
  217. }
  218. }
  219. nearestSearch(self.root);
  220. result = [];
  221. for (i = 0; i < maxNodes; i += 1) {
  222. if (bestNodes.content[i][0]) {
  223. result.push([bestNodes.content[i][0], bestNodes.content[i][1]]);
  224. }
  225. }
  226. return result;
  227. };
  228. }
  229. THREE.TypedArrayUtils.Kdtree.prototype.Node = function(obj, depth, parent, pos) {
  230. this.obj = obj;
  231. this.left = null;
  232. this.right = null;
  233. this.parent = parent;
  234. this.depth = depth;
  235. this.pos = pos;
  236. }
  237. /**
  238. * Binary heap implementation
  239. * @author http://eloquentjavascript.net/appendix2.htm
  240. */
  241. THREE.TypedArrayUtils.Kdtree.BinaryHeap = function(scoreFunction){
  242. this.content = [];
  243. this.scoreFunction = scoreFunction;
  244. }
  245. THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = {
  246. push: function(element) {
  247. // Add the new element to the end of the array.
  248. this.content.push(element);
  249. // Allow it to bubble up.
  250. this.bubbleUp(this.content.length - 1);
  251. },
  252. pop: function() {
  253. // Store the first element so we can return it later.
  254. var result = this.content[0];
  255. // Get the element at the end of the array.
  256. var end = this.content.pop();
  257. // If there are any elements left, put the end element at the
  258. // start, and let it sink down.
  259. if (this.content.length > 0) {
  260. this.content[0] = end;
  261. this.sinkDown(0);
  262. }
  263. return result;
  264. },
  265. peek: function() {
  266. return this.content[0];
  267. },
  268. remove: function(node) {
  269. var len = this.content.length;
  270. // To remove a value, we must search through the array to find
  271. // it.
  272. for (var i = 0; i < len; i++) {
  273. if (this.content[i] == node) {
  274. // When it is found, the process seen in 'pop' is repeated
  275. // to fill up the hole.
  276. var end = this.content.pop();
  277. if (i != len - 1) {
  278. this.content[i] = end;
  279. if (this.scoreFunction(end) < this.scoreFunction(node))
  280. this.bubbleUp(i);
  281. else
  282. this.sinkDown(i);
  283. }
  284. return;
  285. }
  286. }
  287. throw new Error("Node not found.");
  288. },
  289. size: function() {
  290. return this.content.length;
  291. },
  292. bubbleUp: function(n) {
  293. // Fetch the element that has to be moved.
  294. var element = this.content[n];
  295. // When at 0, an element can not go up any further.
  296. while (n > 0) {
  297. // Compute the parent element's index, and fetch it.
  298. var parentN = Math.floor((n + 1) / 2) - 1,
  299. parent = this.content[parentN];
  300. // Swap the elements if the parent is greater.
  301. if (this.scoreFunction(element) < this.scoreFunction(parent)) {
  302. this.content[parentN] = element;
  303. this.content[n] = parent;
  304. // Update 'n' to continue at the new position.
  305. n = parentN;
  306. }
  307. // Found a parent that is less, no need to move it further.
  308. else {
  309. break;
  310. }
  311. }
  312. },
  313. sinkDown: function(n) {
  314. // Look up the target element and its score.
  315. var length = this.content.length,
  316. element = this.content[n],
  317. elemScore = this.scoreFunction(element);
  318. while(true) {
  319. // Compute the indices of the child elements.
  320. var child2N = (n + 1) * 2, child1N = child2N - 1;
  321. // This is used to store the new position of the element,
  322. // if any.
  323. var swap = null;
  324. // If the first child exists (is inside the array)...
  325. if (child1N < length) {
  326. // Look it up and compute its score.
  327. var child1 = this.content[child1N],
  328. child1Score = this.scoreFunction(child1);
  329. // If the score is less than our element's, we need to swap.
  330. if (child1Score < elemScore)
  331. swap = child1N;
  332. }
  333. // Do the same checks for the other child.
  334. if (child2N < length) {
  335. var child2 = this.content[child2N],
  336. child2Score = this.scoreFunction(child2);
  337. if (child2Score < (swap === null ? elemScore : child1Score)){
  338. swap = child2N;
  339. }
  340. }
  341. // If the element needs to be moved, swap it, and continue.
  342. if (swap !== null) {
  343. this.content[n] = this.content[swap];
  344. this.content[swap] = element;
  345. n = swap;
  346. }
  347. // Otherwise, we are done.
  348. else {
  349. break;
  350. }
  351. }
  352. }
  353. };