MortonOrder.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import Check from "./Check.js";
  2. import defined from "./defined.js";
  3. import DeveloperError from "./DeveloperError.js";
  4. /**
  5. * Morton Order (aka Z-Order Curve) helper functions.
  6. * @see {@link https://en.wikipedia.org/wiki/Z-order_curve}
  7. *
  8. * @namespace MortonOrder
  9. * @private
  10. */
  11. const MortonOrder = {};
  12. /**
  13. * Inserts one 0 bit of spacing between a number's bits. This is the opposite of removeOneSpacing.
  14. *
  15. * Example:
  16. * input: 6
  17. * input (binary): 110
  18. * output (binary): 10100
  19. * ^ ^ (added)
  20. * output: 20
  21. *
  22. * @private
  23. * @param {Number} v A 16-bit unsigned integer.
  24. * @returns {Number} A 32-bit unsigned integer.
  25. * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/}
  26. * @private
  27. */
  28. function insertOneSpacing(v) {
  29. v = (v ^ (v << 8)) & 0x00ff00ff;
  30. v = (v ^ (v << 4)) & 0x0f0f0f0f;
  31. v = (v ^ (v << 2)) & 0x33333333;
  32. v = (v ^ (v << 1)) & 0x55555555;
  33. return v;
  34. }
  35. /**
  36. * Inserts two 0 bits of spacing between a number's bits. This is the opposite of removeTwoSpacing.
  37. *
  38. * Example:
  39. * input: 6
  40. * input (binary): 110
  41. * output (binary): 1001000
  42. * ^^ ^^ (added)
  43. * output: 72
  44. *
  45. * @private
  46. * @param {Number} v A 10-bit unsigned integer.
  47. * @returns {Number} A 30-bit unsigned integer.
  48. * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/}
  49. */
  50. function insertTwoSpacing(v) {
  51. v = (v ^ (v << 16)) & 0x030000ff;
  52. v = (v ^ (v << 8)) & 0x0300f00f;
  53. v = (v ^ (v << 4)) & 0x030c30c3;
  54. v = (v ^ (v << 2)) & 0x09249249;
  55. return v;
  56. }
  57. /**
  58. * Removes one bit of spacing between bits. This is the opposite of insertOneSpacing.
  59. *
  60. * Example:
  61. * input: 20
  62. * input (binary): 10100
  63. * ^ ^ (removed)
  64. * output (binary): 110
  65. * output: 6
  66. *
  67. * @private
  68. * @param {Number} v A 32-bit unsigned integer.
  69. * @returns {Number} A 16-bit unsigned integer.
  70. * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/}
  71. */
  72. function removeOneSpacing(v) {
  73. v &= 0x55555555;
  74. v = (v ^ (v >> 1)) & 0x33333333;
  75. v = (v ^ (v >> 2)) & 0x0f0f0f0f;
  76. v = (v ^ (v >> 4)) & 0x00ff00ff;
  77. v = (v ^ (v >> 8)) & 0x0000ffff;
  78. return v;
  79. }
  80. /**
  81. * Removes two bits of spacing between bits. This is the opposite of insertTwoSpacing.
  82. *
  83. * Example:
  84. * input: 72
  85. * input (binary): 1001000
  86. * ^^ ^^ (removed)
  87. * output (binary): 110
  88. * output: 6
  89. *
  90. * @private
  91. * @param {Number} v A 30-bit unsigned integer.
  92. * @returns {Number} A 10-bit unsigned integer.
  93. * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/}
  94. */
  95. function removeTwoSpacing(v) {
  96. v &= 0x09249249;
  97. v = (v ^ (v >> 2)) & 0x030c30c3;
  98. v = (v ^ (v >> 4)) & 0x0300f00f;
  99. v = (v ^ (v >> 8)) & 0xff0000ff;
  100. v = (v ^ (v >> 16)) & 0x000003ff;
  101. return v;
  102. }
  103. /**
  104. * Computes the Morton index from 2D coordinates. This is equivalent to interleaving their bits.
  105. * The inputs must be 16-bit unsigned integers (resulting in 32-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript.
  106. *
  107. * @param {Number} x The X coordinate in the range [0, (2^16)-1].
  108. * @param {Number} y The Y coordinate in the range [0, (2^16)-1].
  109. * @returns {Number} The Morton index.
  110. * @private
  111. */
  112. MortonOrder.encode2D = function (x, y) {
  113. //>>includeStart('debug', pragmas.debug);
  114. Check.typeOf.number("x", x);
  115. Check.typeOf.number("y", y);
  116. if (x < 0 || x > 65535 || y < 0 || y > 65535) {
  117. throw new DeveloperError("inputs must be 16-bit unsigned integers");
  118. }
  119. //>>includeEnd('debug');
  120. // Note: JavaScript bitwise operations return signed 32-bit integers, so the
  121. // final result needs to be reintepreted as an unsigned integer using >>> 0.
  122. // This is not needed for encode3D because the result is guaranteed to be at most
  123. // 30 bits and thus will always be interpreted as an unsigned value.
  124. return (insertOneSpacing(x) | (insertOneSpacing(y) << 1)) >>> 0;
  125. };
  126. /**
  127. * Computes the 2D coordinates from a Morton index. This is equivalent to deinterleaving their bits.
  128. * The input must be a 32-bit unsigned integer (resulting in 16 bits per coordinate) due to 32-bit bitwise operator limitation in JavaScript.
  129. *
  130. * @param {Number} mortonIndex The Morton index in the range [0, (2^32)-1].
  131. * @param {Number[]} [result] The array onto which to store the result.
  132. * @returns {Number[]} An array containing the 2D coordinates correspoding to the Morton index.
  133. * @private
  134. */
  135. MortonOrder.decode2D = function (mortonIndex, result) {
  136. //>>includeStart('debug', pragmas.debug);
  137. Check.typeOf.number("mortonIndex", mortonIndex);
  138. if (mortonIndex < 0 || mortonIndex > 4294967295) {
  139. throw new DeveloperError("input must be a 32-bit unsigned integer");
  140. }
  141. //>>includeEnd('debug');
  142. if (!defined(result)) {
  143. result = new Array(2);
  144. }
  145. result[0] = removeOneSpacing(mortonIndex);
  146. result[1] = removeOneSpacing(mortonIndex >> 1);
  147. return result;
  148. };
  149. /**
  150. * Computes the Morton index from 3D coordinates. This is equivalent to interleaving their bits.
  151. * The inputs must be 10-bit unsigned integers (resulting in 30-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript.
  152. *
  153. * @param {Number} x The X coordinate in the range [0, (2^10)-1].
  154. * @param {Number} y The Y coordinate in the range [0, (2^10)-1].
  155. * @param {Number} z The Z coordinate in the range [0, (2^10)-1].
  156. * @returns {Number} The Morton index.
  157. * @private
  158. */
  159. MortonOrder.encode3D = function (x, y, z) {
  160. //>>includeStart('debug', pragmas.debug);
  161. Check.typeOf.number("x", x);
  162. Check.typeOf.number("y", y);
  163. Check.typeOf.number("z", z);
  164. if (x < 0 || x > 1023 || y < 0 || y > 1023 || z < 0 || z > 1023) {
  165. throw new DeveloperError("inputs must be 10-bit unsigned integers");
  166. }
  167. //>>includeEnd('debug');
  168. return (
  169. insertTwoSpacing(x) |
  170. (insertTwoSpacing(y) << 1) |
  171. (insertTwoSpacing(z) << 2)
  172. );
  173. };
  174. /**
  175. * Computes the 3D coordinates from a Morton index. This is equivalent to deinterleaving their bits.
  176. * The input must be a 30-bit unsigned integer (resulting in 10 bits per coordinate) due to 32-bit bitwise operator limitation in JavaScript.
  177. *
  178. * @param {Number} mortonIndex The Morton index in the range [0, (2^30)-1].
  179. * @param {Number[]} [result] The array onto which to store the result.
  180. * @returns {Number[]} An array containing the 3D coordinates corresponding to the Morton index.
  181. * @private
  182. */
  183. MortonOrder.decode3D = function (mortonIndex, result) {
  184. //>>includeStart('debug', pragmas.debug);
  185. Check.typeOf.number("mortonIndex", mortonIndex);
  186. if (mortonIndex < 0 || mortonIndex > 1073741823) {
  187. throw new DeveloperError("input must be a 30-bit unsigned integer");
  188. }
  189. //>>includeEnd('debug');
  190. if (!defined(result)) {
  191. result = new Array(3);
  192. }
  193. result[0] = removeTwoSpacing(mortonIndex);
  194. result[1] = removeTwoSpacing(mortonIndex >> 1);
  195. result[2] = removeTwoSpacing(mortonIndex >> 2);
  196. return result;
  197. };
  198. export default MortonOrder;