SortUtils.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * @module SortUtils
  3. * @three_import import * as SortUtils from 'three/addons/utils/SortUtils.js';
  4. */
  5. const POWER = 3;
  6. const BIT_MAX = 32;
  7. const BIN_BITS = 1 << POWER;
  8. const BIN_SIZE = 1 << BIN_BITS;
  9. const BIN_MAX = BIN_SIZE - 1;
  10. const ITERATIONS = BIT_MAX / BIN_BITS;
  11. const bins = new Array( ITERATIONS );
  12. const bins_buffer = new ArrayBuffer( ( ITERATIONS + 1 ) * BIN_SIZE * 4 );
  13. let c = 0;
  14. for ( let i = 0; i < ( ITERATIONS + 1 ); i ++ ) {
  15. bins[ i ] = new Uint32Array( bins_buffer, c, BIN_SIZE );
  16. c += BIN_SIZE * 4;
  17. }
  18. const defaultGet = ( el ) => el;
  19. /**
  20. * Hybrid radix sort from.
  21. *
  22. * - {@link https://gist.github.com/sciecode/93ed864dd77c5c8803c6a86698d68dab}
  23. * - {@link https://github.com/mrdoob/three.js/pull/27202#issuecomment-1817640271}
  24. *
  25. * Expects unsigned 32b integer values.
  26. *
  27. * @function
  28. * @param {Array<Object>} arr - The array to sort.
  29. * @param {Object} opt - The options
  30. */
  31. export const radixSort = ( arr, opt ) => {
  32. const len = arr.length;
  33. const options = opt || {};
  34. const aux = options.aux || new arr.constructor( len );
  35. const get = options.get || defaultGet;
  36. const data = [ arr, aux ];
  37. let compare, accumulate, recurse;
  38. if ( options.reversed ) {
  39. compare = ( a, b ) => a < b;
  40. accumulate = ( bin ) => {
  41. for ( let j = BIN_SIZE - 2; j >= 0; j -- )
  42. bin[ j ] += bin[ j + 1 ];
  43. };
  44. recurse = ( cache, depth, start ) => {
  45. let prev = 0;
  46. for ( let j = BIN_MAX; j >= 0; j -- ) {
  47. const cur = cache[ j ], diff = cur - prev;
  48. if ( diff != 0 ) {
  49. if ( diff > 32 )
  50. radixSortBlock( depth + 1, start + prev, diff );
  51. else
  52. insertionSortBlock( depth + 1, start + prev, diff );
  53. prev = cur;
  54. }
  55. }
  56. };
  57. } else {
  58. compare = ( a, b ) => a > b;
  59. accumulate = ( bin ) => {
  60. for ( let j = 1; j < BIN_SIZE; j ++ )
  61. bin[ j ] += bin[ j - 1 ];
  62. };
  63. recurse = ( cache, depth, start ) => {
  64. let prev = 0;
  65. for ( let j = 0; j < BIN_SIZE; j ++ ) {
  66. const cur = cache[ j ], diff = cur - prev;
  67. if ( diff != 0 ) {
  68. if ( diff > 32 )
  69. radixSortBlock( depth + 1, start + prev, diff );
  70. else
  71. insertionSortBlock( depth + 1, start + prev, diff );
  72. prev = cur;
  73. }
  74. }
  75. };
  76. }
  77. const insertionSortBlock = ( depth, start, len ) => {
  78. const a = data[ depth & 1 ];
  79. const b = data[ ( depth + 1 ) & 1 ];
  80. for ( let j = start + 1; j < start + len; j ++ ) {
  81. const p = a[ j ], t = get( p ) >>> 0;
  82. let i = j;
  83. while ( i > start ) {
  84. if ( compare( get( a[ i - 1 ] ) >>> 0, t ) )
  85. a[ i ] = a[ -- i ];
  86. else
  87. break;
  88. }
  89. a[ i ] = p;
  90. }
  91. if ( ( depth & 1 ) == 1 ) {
  92. for ( let i = start; i < start + len; i ++ )
  93. b[ i ] = a[ i ];
  94. }
  95. };
  96. const radixSortBlock = ( depth, start, len ) => {
  97. const a = data[ depth & 1 ];
  98. const b = data[ ( depth + 1 ) & 1 ];
  99. const shift = ( 3 - depth ) << POWER;
  100. const end = start + len;
  101. const cache = bins[ depth ];
  102. const bin = bins[ depth + 1 ];
  103. bin.fill( 0 );
  104. for ( let j = start; j < end; j ++ )
  105. bin[ ( get( a[ j ] ) >>> shift ) & BIN_MAX ] ++;
  106. accumulate( bin );
  107. cache.set( bin );
  108. for ( let j = end - 1; j >= start; j -- )
  109. b[ start + -- bin[ ( get( a[ j ] ) >>> shift ) & BIN_MAX ] ] = a[ j ];
  110. if ( depth == ITERATIONS - 1 ) return;
  111. recurse( cache, depth, start );
  112. };
  113. radixSortBlock( 0, 0, len );
  114. };