TypedArrayUtils.js 11 KB

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