@tty-pt/islet 0.0.0-stage → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +98 -2
- package/assets/logo.png +0 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +110 -0
- package/dist/islet.wasm +0 -0
- package/include/ttypt/islet.h +1187 -0
- package/include/ttypt/morton.h +536 -0
- package/include/ttypt/point.h +696 -0
- package/include/ttypt/pointcfg.h +200 -0
- package/islet.pc +10 -0
- package/package.json +50 -4
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
#ifndef MORTON_H
|
|
2
|
+
#define MORTON_H
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file morton.h
|
|
6
|
+
* @brief Morton code (Z-order) encoding and decoding utilities.
|
|
7
|
+
*
|
|
8
|
+
* Morton codes, also known as Z-order codes, are a way to map multi-dimensional
|
|
9
|
+
* coordinates to a single dimension while preserving spatial locality. They
|
|
10
|
+
* interleave the binary representations of each coordinate dimension, creating
|
|
11
|
+
* a space-filling curve that visits all points in a recursive Z pattern.
|
|
12
|
+
*
|
|
13
|
+
* Benefits:
|
|
14
|
+
* - Preserves spatial locality: nearby points in N-D space have nearby Morton codes
|
|
15
|
+
* - Enables efficient range queries on 1D data structures (hash tables, B-trees)
|
|
16
|
+
* - Simple bitwise operations for encoding/decoding
|
|
17
|
+
*
|
|
18
|
+
* This implementation is optimized for 3D coordinates (int16_t per dimension)
|
|
19
|
+
* and produces 64-bit Morton codes (48 bits used for 3D, 16 bits reserved).
|
|
20
|
+
* 4D coordinates use a dense stride-4 packing that fills all 64 bits.
|
|
21
|
+
* The 2D x 32-bit config (int32_t per dimension) is the dense
|
|
22
|
+
* stride-2 partner: 2 x 32 = 64 bits, no reserved bits.
|
|
23
|
+
*
|
|
24
|
+
* Prefer the config objects (pointcfg.h: Point1_2..Point4_2, Point2_4)
|
|
25
|
+
* in application code; these inlines remain the tight-loop fast path.
|
|
26
|
+
*
|
|
27
|
+
* References:
|
|
28
|
+
* - Morton, G.M. (1966). "A computer Oriented Geodetic Data Base"
|
|
29
|
+
* - http://www.vision-tools.com/h-tropf/multidimensionalrangequery.pdf
|
|
30
|
+
* - https://en.wikipedia.org/wiki/Z-order_curve
|
|
31
|
+
*
|
|
32
|
+
* @see islet_core
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
#include <stdint.h>
|
|
36
|
+
#include <limits.h>
|
|
37
|
+
|
|
38
|
+
/* Optimization tunable — a 0/1 flag. ISLET_USE_PDEP gates the BMI2
|
|
39
|
+
* PDEP/PEXT Morton kernels; it defaults to 1 and only activates when the
|
|
40
|
+
* TU is compiled with -mbmi2 (macro __BMI2__). Override with
|
|
41
|
+
* -DISLET_USE_PDEP=0/1. Outputs are bit-identical to the scalar kernels
|
|
42
|
+
* either way; see docs/PERF.md. On AMD Zen ≤3 the PDEP instruction
|
|
43
|
+
* is microcoded and may be slower than the scalar path — opt out
|
|
44
|
+
* there. */
|
|
45
|
+
#ifndef ISLET_USE_PDEP
|
|
46
|
+
#define ISLET_USE_PDEP 1
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
/** @defgroup islet_morton Morton code helpers
|
|
50
|
+
* @brief Encode and decode Morton (Z-order) codes for spatial indexing.
|
|
51
|
+
*
|
|
52
|
+
* Morton codes interleave the bits of multi-dimensional coordinates to create
|
|
53
|
+
* a single linear ordering that preserves spatial locality. For example, in 2D:
|
|
54
|
+
*
|
|
55
|
+
* Coordinate (x=5, y=3):
|
|
56
|
+
* - x = 5 = 0b0101 (binary)
|
|
57
|
+
* - y = 3 = 0b0011 (binary)
|
|
58
|
+
* - Interleaved: 0b00110011 = 51 (y bits in odd positions, x in even)
|
|
59
|
+
*
|
|
60
|
+
* In 3D, the pattern extends with z bits in every third position.
|
|
61
|
+
*
|
|
62
|
+
* Coordinate System:
|
|
63
|
+
* - Input: int16_t signed coordinates (-32768 to 32767)
|
|
64
|
+
* - Internal: uint16_t unsigned coordinates (0 to 65535) via offset mapping
|
|
65
|
+
* - Output: uint64_t Morton code (48 bits for 3D with the upper 16 bits
|
|
66
|
+
* reserved; all 64 bits for 4D)
|
|
67
|
+
*
|
|
68
|
+
* The conversion from signed to unsigned preserves ordering:
|
|
69
|
+
* -32768 → 0, -32767 → 1, ..., 0 → 32768, ..., 32767 → 65535
|
|
70
|
+
*
|
|
71
|
+
* @note Currently optimized for 3D/4D with fast bit-manipulation
|
|
72
|
+
* algorithms (stride-3 packing for dims 1-3, dense stride-4
|
|
73
|
+
* packing for dim 4, dense stride-2 packing for the 2D x 32-bit
|
|
74
|
+
* config). 1D/2D/3D codes are bit-identical to
|
|
75
|
+
* v0.5.0; 4D codes are new. When compiled with -mbmi2 the encode
|
|
76
|
+
* and decode kernels use PDEP/PEXT for a 2-3× speedup over the
|
|
77
|
+
* scalar path (default-on, opt out with -DISLET_USE_PDEP=0).
|
|
78
|
+
*
|
|
79
|
+
* @note All dimensions share the uint64 key space: use one dimension
|
|
80
|
+
* per database.
|
|
81
|
+
*
|
|
82
|
+
* @see islet_core
|
|
83
|
+
* @see islet_point
|
|
84
|
+
* @{
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/* When libislet.c builds the external ABI wrappers it defines
|
|
88
|
+
* ISLET_MORTON_RENAME_FOR_WRAPPERS before including this header, so the
|
|
89
|
+
* static inline versions take the _il names and don't clash with the
|
|
90
|
+
* extern definitions in that TU. Consumers never define it. */
|
|
91
|
+
#ifdef ISLET_MORTON_RENAME_FOR_WRAPPERS
|
|
92
|
+
#define morton_set_1 morton_set_1_il
|
|
93
|
+
#define morton_set_2 morton_set_2_il
|
|
94
|
+
#define morton_set_3 morton_set_3_il
|
|
95
|
+
#define morton_set_4 morton_set_4_il
|
|
96
|
+
#define morton_set_2_32 morton_set_2_32_il
|
|
97
|
+
#define morton_get_1 morton_get_1_il
|
|
98
|
+
#define morton_get_2 morton_get_2_il
|
|
99
|
+
#define morton_get_3 morton_get_3_il
|
|
100
|
+
#define morton_get_4 morton_get_4_il
|
|
101
|
+
#define morton_get_2_32 morton_get_2_32_il
|
|
102
|
+
#endif
|
|
103
|
+
|
|
104
|
+
static inline uint16_t
|
|
105
|
+
islet_unsign(int16_t n)
|
|
106
|
+
{
|
|
107
|
+
return (uint16_t)(n + SHRT_MAX + 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static inline int16_t
|
|
111
|
+
islet_sign(uint16_t n)
|
|
112
|
+
{
|
|
113
|
+
return (int16_t)(n - SHRT_MAX - 1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* 32-bit-lane bias helpers for the 2D x 32-bit dense config. The
|
|
117
|
+
* unsigned lane is the full uint32 range: -2^31 -> 0, ..., 0 ->
|
|
118
|
+
* 0x80000000, ..., 2^31-1 -> 0xFFFFFFFF. All arithmetic is modulo
|
|
119
|
+
* 2^32, so the add/sub wrap exactly like the 16-bit pair above. */
|
|
120
|
+
static inline uint32_t
|
|
121
|
+
islet_unsign32(int32_t n)
|
|
122
|
+
{
|
|
123
|
+
return (uint32_t)n + 0x80000000u;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static inline int32_t
|
|
127
|
+
islet_sign32(uint32_t n)
|
|
128
|
+
{
|
|
129
|
+
return (int32_t)(n - 0x80000000u);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static inline uint64_t
|
|
133
|
+
islet_spread3(uint32_t x)
|
|
134
|
+
{
|
|
135
|
+
uint64_t v = x & 0xFFFFu;
|
|
136
|
+
|
|
137
|
+
v = (v | (v << 32)) & 0x1F00000000FFFFULL;
|
|
138
|
+
v = (v | (v << 16)) & 0x1F0000FF0000FFULL;
|
|
139
|
+
v = (v | (v << 8)) & 0x100F00F00F00F00FULL;
|
|
140
|
+
v = (v | (v << 4)) & 0x10C30C30C30C30C3ULL;
|
|
141
|
+
v = (v | (v << 2)) & 0x1249249249249249ULL;
|
|
142
|
+
|
|
143
|
+
return v;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static inline uint64_t
|
|
147
|
+
islet_compact_axis(uint64_t code, uint32_t shift)
|
|
148
|
+
{
|
|
149
|
+
code >>= shift;
|
|
150
|
+
code &= 0x1249249249249249ULL;
|
|
151
|
+
code = (code ^ (code >> 2)) & 0x10C30C30C30C30C3ULL;
|
|
152
|
+
code = (code ^ (code >> 4)) & 0x100F00F00F00F00FULL;
|
|
153
|
+
code = (code ^ (code >> 8)) & 0x1F0000FF0000FFULL;
|
|
154
|
+
code = (code ^ (code >> 16)) & 0x1F00000000FFFFULL;
|
|
155
|
+
code = (code ^ (code >> 32)) & 0x00000000001FFFFFULL;
|
|
156
|
+
return (uint32_t) code;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* spread2(x):
|
|
160
|
+
* Take x in [0..0xFFFFFFFF] and produce a 64-bit word where its
|
|
161
|
+
* bit-i goes to bit-(2*i) in the result (even positions). 2D x 32-bit
|
|
162
|
+
* needs all 64 bits (2 x 32), so there are no reserved top bits.
|
|
163
|
+
*
|
|
164
|
+
* Part of a Morton-2D32 encode: code = spread2(x) | spread2(y)<<1
|
|
165
|
+
*/
|
|
166
|
+
static inline uint64_t
|
|
167
|
+
islet_spread2(uint32_t x)
|
|
168
|
+
{
|
|
169
|
+
uint64_t v = x;
|
|
170
|
+
|
|
171
|
+
v = (v | (v << 16)) & 0x0000FFFF0000FFFFULL;
|
|
172
|
+
v = (v | (v << 8)) & 0x00FF00FF00FF00FFULL;
|
|
173
|
+
v = (v | (v << 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
|
174
|
+
v = (v | (v << 2)) & 0x3333333333333333ULL;
|
|
175
|
+
v = (v | (v << 1)) & 0x5555555555555555ULL;
|
|
176
|
+
|
|
177
|
+
return v;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* compact_axis2(): collect one out of every 2 bits from 'code',
|
|
181
|
+
* starting at 'shift' (0 = x, 1 = y).
|
|
182
|
+
* Returns the low-order 32 bits containing that coordinate.
|
|
183
|
+
*/
|
|
184
|
+
static inline uint64_t
|
|
185
|
+
islet_compact_axis2(uint64_t code, uint32_t shift)
|
|
186
|
+
{
|
|
187
|
+
code >>= shift;
|
|
188
|
+
code &= 0x5555555555555555ULL;
|
|
189
|
+
code = (code ^ (code >> 1)) & 0x3333333333333333ULL;
|
|
190
|
+
code = (code ^ (code >> 2)) & 0x0F0F0F0F0F0F0F0FULL;
|
|
191
|
+
code = (code ^ (code >> 4)) & 0x00FF00FF00FF00FFULL;
|
|
192
|
+
code = (code ^ (code >> 8)) & 0x0000FFFF0000FFFFULL;
|
|
193
|
+
code = (code ^ (code >> 16)) & 0x00000000FFFFFFFFULL;
|
|
194
|
+
return (uint32_t) code;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* spread4(x):
|
|
198
|
+
* Take x ∈ [0..0xFFFF] and produce a 64-bit word where
|
|
199
|
+
* its bit-i goes to bit-(4*i) in the result. 4D needs all 64 bits
|
|
200
|
+
* (4 × 16), so unlike the 3D path there are no reserved top bits.
|
|
201
|
+
*
|
|
202
|
+
* Part of a Morton-4D encode: code = spread4(x)
|
|
203
|
+
* | spread4(y)<<1
|
|
204
|
+
* | spread4(z)<<2
|
|
205
|
+
* | spread4(w)<<3
|
|
206
|
+
*/
|
|
207
|
+
static inline uint64_t
|
|
208
|
+
islet_spread4(uint32_t x)
|
|
209
|
+
{
|
|
210
|
+
uint64_t v = x & 0xFFFFu;
|
|
211
|
+
|
|
212
|
+
/* separate high/low bytes with a 24-bit gap */
|
|
213
|
+
v = (v | (v << 24)) & 0xFF000000FFULL;
|
|
214
|
+
/* down to nibbles */
|
|
215
|
+
v = (v | (v << 12)) & 0x000F000F000F000FULL;
|
|
216
|
+
/* down to bit pairs */
|
|
217
|
+
v = (v | (v << 6)) & 0x0303030303030303ULL;
|
|
218
|
+
/* final 4-bit interleave */
|
|
219
|
+
v = (v | (v << 3)) & 0x1111111111111111ULL;
|
|
220
|
+
|
|
221
|
+
return v;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* compact_axis4(): collect one out of every 4 bits from 'code',
|
|
225
|
+
* starting at 'shift' (0 = x, 1 = y, 2 = z, 3 = w).
|
|
226
|
+
* Returns the low-order 16 bits containing that coordinate.
|
|
227
|
+
*/
|
|
228
|
+
static inline uint64_t
|
|
229
|
+
islet_compact_axis4(uint64_t code, uint32_t shift)
|
|
230
|
+
{
|
|
231
|
+
code >>= shift;
|
|
232
|
+
code &= 0x1111111111111111ULL;
|
|
233
|
+
code = (code ^ (code >> 3)) & 0x0303030303030303ULL;
|
|
234
|
+
code = (code ^ (code >> 6)) & 0x000F000F000F000FULL;
|
|
235
|
+
code = (code ^ (code >> 12)) & 0x000000FF000000FFULL;
|
|
236
|
+
code = (code ^ (code >> 24)) & 0x000000000000FFFFULL;
|
|
237
|
+
return (uint16_t) code;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static inline void
|
|
241
|
+
islet_decode3(uint64_t code, uint32_t *x, uint32_t *y, uint32_t *z)
|
|
242
|
+
{
|
|
243
|
+
static const uint64_t mask_off = 0x0000FFFFFFFFFFFFULL;
|
|
244
|
+
code &= mask_off;
|
|
245
|
+
*x = islet_compact_axis(code, 0);
|
|
246
|
+
*y = islet_compact_axis(code, 1);
|
|
247
|
+
*z = islet_compact_axis(code, 2);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static inline void
|
|
251
|
+
islet_decode4(uint64_t code,
|
|
252
|
+
uint32_t *x, uint32_t *y, uint32_t *z, uint32_t *w)
|
|
253
|
+
{
|
|
254
|
+
*x = (uint32_t)islet_compact_axis4(code, 0);
|
|
255
|
+
*y = (uint32_t)islet_compact_axis4(code, 1);
|
|
256
|
+
*z = (uint32_t)islet_compact_axis4(code, 2);
|
|
257
|
+
*w = (uint32_t)islet_compact_axis4(code, 3);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @brief Encode multi-dimensional coordinates into Morton codes.
|
|
262
|
+
*
|
|
263
|
+
* These per-dimension specializations are the encode API. Each encodes
|
|
264
|
+
* exactly one dimension count; there is no runtime `dim` argument to
|
|
265
|
+
* branch on, so the compiler sees a fully unrolled expression.
|
|
266
|
+
*
|
|
267
|
+
* Algorithm (dims 1-3):
|
|
268
|
+
* 1. Convert signed int16_t to unsigned uint16_t (add 32768)
|
|
269
|
+
* 2. Spread each 16-bit coordinate across 48 bits (every 3rd bit)
|
|
270
|
+
* 3. Combine: code = spread(x) | spread(y)<<1 | spread(z)<<2
|
|
271
|
+
*
|
|
272
|
+
* Dim 4 uses dense stride-4 packing that fills all 64 bits. Dims 1-3
|
|
273
|
+
* code values are bit-identical to v0.5.0; 4D codes are new.
|
|
274
|
+
*
|
|
275
|
+
* @param[in] p Input point. Array of int16_t with at least the
|
|
276
|
+
* dimension count of the function used. Coordinates range
|
|
277
|
+
* from -32768 to 32767.
|
|
278
|
+
*
|
|
279
|
+
* @return 64-bit Morton code. For 1-3D, uses 48 bits (16 bits per
|
|
280
|
+
* dimension) with the upper 16 bits reserved; 4D fills all 64
|
|
281
|
+
* bits.
|
|
282
|
+
*
|
|
283
|
+
* Example (3D):
|
|
284
|
+
* @code
|
|
285
|
+
* int16_t point[3] = {10, -5, 100};
|
|
286
|
+
* uint64_t code = morton_set_3(point);
|
|
287
|
+
* // code now contains the interleaved bit representation
|
|
288
|
+
* @endcode
|
|
289
|
+
*
|
|
290
|
+
* Example (round-trip verification):
|
|
291
|
+
* @code
|
|
292
|
+
* int16_t original[3] = {123, -456, 789};
|
|
293
|
+
* uint64_t code = morton_set_3(original);
|
|
294
|
+
* int16_t decoded[3];
|
|
295
|
+
* morton_get_3(decoded, code);
|
|
296
|
+
* // decoded[0]==123, decoded[1]==-456, decoded[2]==789
|
|
297
|
+
* @endcode
|
|
298
|
+
*
|
|
299
|
+
* @see morton_set_1 morton_set_2 morton_set_4
|
|
300
|
+
* @see morton_get_1 morton_get_2 morton_get_3 morton_get_4
|
|
301
|
+
* @see islet_put
|
|
302
|
+
* @see islet_get
|
|
303
|
+
*/
|
|
304
|
+
static inline uint64_t
|
|
305
|
+
morton_set_1(int16_t *p)
|
|
306
|
+
{
|
|
307
|
+
uint16_t up0 = islet_unsign(p[0]);
|
|
308
|
+
|
|
309
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
310
|
+
return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL);
|
|
311
|
+
#else
|
|
312
|
+
return islet_spread3(up0);
|
|
313
|
+
#endif
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @brief Encode a 2D point. See morton_set_1() for the family docs.
|
|
318
|
+
*/
|
|
319
|
+
static inline uint64_t
|
|
320
|
+
morton_set_2(int16_t *p)
|
|
321
|
+
{
|
|
322
|
+
uint16_t up0 = islet_unsign(p[0]);
|
|
323
|
+
uint16_t up1 = islet_unsign(p[1]);
|
|
324
|
+
|
|
325
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
326
|
+
return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL)
|
|
327
|
+
| __builtin_ia32_pdep_di(up1, 0x2492492492492492ULL);
|
|
328
|
+
#else
|
|
329
|
+
return islet_spread3(up0) | (islet_spread3(up1) << 1);
|
|
330
|
+
#endif
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @brief Encode a 3D point. See morton_set_1() for the family docs.
|
|
335
|
+
*/
|
|
336
|
+
static inline uint64_t
|
|
337
|
+
morton_set_3(int16_t *p)
|
|
338
|
+
{
|
|
339
|
+
uint16_t up0 = islet_unsign(p[0]);
|
|
340
|
+
uint16_t up1 = islet_unsign(p[1]);
|
|
341
|
+
uint16_t up2 = islet_unsign(p[2]);
|
|
342
|
+
|
|
343
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
344
|
+
return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL)
|
|
345
|
+
| __builtin_ia32_pdep_di(up1, 0x2492492492492492ULL)
|
|
346
|
+
| __builtin_ia32_pdep_di(up2, 0x4924924924924924ULL);
|
|
347
|
+
#else
|
|
348
|
+
return islet_spread3(up0)
|
|
349
|
+
| (islet_spread3(up1) << 1)
|
|
350
|
+
| (islet_spread3(up2) << 2);
|
|
351
|
+
#endif
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @brief Encode a 4D point. See morton_set_1() for the family docs.
|
|
356
|
+
*/
|
|
357
|
+
static inline uint64_t
|
|
358
|
+
morton_set_4(int16_t *p)
|
|
359
|
+
{
|
|
360
|
+
uint16_t up0 = islet_unsign(p[0]);
|
|
361
|
+
uint16_t up1 = islet_unsign(p[1]);
|
|
362
|
+
uint16_t up2 = islet_unsign(p[2]);
|
|
363
|
+
uint16_t up3 = islet_unsign(p[3]);
|
|
364
|
+
|
|
365
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
366
|
+
return __builtin_ia32_pdep_di(up0, 0x1111111111111111ULL)
|
|
367
|
+
| __builtin_ia32_pdep_di(up1, 0x2222222222222222ULL)
|
|
368
|
+
| __builtin_ia32_pdep_di(up2, 0x4444444444444444ULL)
|
|
369
|
+
| __builtin_ia32_pdep_di(up3, 0x8888888888888888ULL);
|
|
370
|
+
#else
|
|
371
|
+
return islet_spread4(up0)
|
|
372
|
+
| (islet_spread4(up1) << 1)
|
|
373
|
+
| (islet_spread4(up2) << 2)
|
|
374
|
+
| (islet_spread4(up3) << 3);
|
|
375
|
+
#endif
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @brief Encode a 2D x 32-bit point. Dense full-64-bit layout
|
|
380
|
+
* (2 x 32 bits); part of the dense config family, not the
|
|
381
|
+
* legacy 16-bit-lane sparse layout that morton_set_2() uses.
|
|
382
|
+
*
|
|
383
|
+
* Algorithm:
|
|
384
|
+
* 1. Convert signed int32_t to unsigned uint32_t (add 2^31)
|
|
385
|
+
* 2. Spread each 32-bit coordinate across 64 bits (every 2nd bit)
|
|
386
|
+
* 3. Combine: code = spread2(x) | spread2(y)<<1
|
|
387
|
+
*
|
|
388
|
+
* @param[in] p Input point. Array of int32_t with at least 2 elements.
|
|
389
|
+
* Coordinates range from -2147483648 to 2147483647.
|
|
390
|
+
*
|
|
391
|
+
* @return 64-bit Morton code filling all 64 bits (no reserved bits).
|
|
392
|
+
*/
|
|
393
|
+
static inline uint64_t
|
|
394
|
+
morton_set_2_32(int32_t *p)
|
|
395
|
+
{
|
|
396
|
+
uint32_t up0 = islet_unsign32(p[0]);
|
|
397
|
+
uint32_t up1 = islet_unsign32(p[1]);
|
|
398
|
+
|
|
399
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
400
|
+
return __builtin_ia32_pdep_di(up0, 0x5555555555555555ULL)
|
|
401
|
+
| __builtin_ia32_pdep_di(up1, 0xAAAAAAAAAAAAAAAAULL);
|
|
402
|
+
#else
|
|
403
|
+
return islet_spread2(up0) | (islet_spread2(up1) << 1);
|
|
404
|
+
#endif
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @brief Decode a Morton code into a multi-dimensional coordinate.
|
|
409
|
+
*
|
|
410
|
+
* These per-dimension specializations are the decode API. Each decodes
|
|
411
|
+
* exactly one dimension count; there is no runtime `dim` argument.
|
|
412
|
+
* Inverse of the matching morton_set_N().
|
|
413
|
+
*
|
|
414
|
+
* @param[out] pos Output point. Array of int16_t with space for the
|
|
415
|
+
* dimension count of the function used.
|
|
416
|
+
* @param[in] code Morton code to decode (64-bit).
|
|
417
|
+
*
|
|
418
|
+
* @note The output coordinates will be in range -32768 to 32767 (int16_t).
|
|
419
|
+
*
|
|
420
|
+
* @warning Decoding a Morton code that wasn't created by morton_set_N()
|
|
421
|
+
* with valid coordinates may produce unexpected results (garbage
|
|
422
|
+
* coordinates).
|
|
423
|
+
*
|
|
424
|
+
* Example:
|
|
425
|
+
* @code
|
|
426
|
+
* uint64_t code = 0x123456789ABCULL; // Some Morton code
|
|
427
|
+
* int16_t point[3];
|
|
428
|
+
* morton_get_3(point, code);
|
|
429
|
+
* // point now contains the decoded coordinates
|
|
430
|
+
* @endcode
|
|
431
|
+
*
|
|
432
|
+
* @see morton_get_1 morton_get_2 morton_get_4
|
|
433
|
+
* @see morton_set_1 morton_set_2 morton_set_3 morton_set_4
|
|
434
|
+
* @see islet_iter
|
|
435
|
+
*/
|
|
436
|
+
static inline void
|
|
437
|
+
morton_get_1(int16_t *pos, uint64_t code)
|
|
438
|
+
{
|
|
439
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
440
|
+
pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
441
|
+
0x1249249249249249ULL));
|
|
442
|
+
#else
|
|
443
|
+
pos[0] = islet_sign((uint16_t)islet_compact_axis(code, 0));
|
|
444
|
+
#endif
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* @brief Decode a 2D Morton code. See morton_get_1() for the family docs.
|
|
449
|
+
*/
|
|
450
|
+
static inline void
|
|
451
|
+
morton_get_2(int16_t *pos, uint64_t code)
|
|
452
|
+
{
|
|
453
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
454
|
+
pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
455
|
+
0x1249249249249249ULL));
|
|
456
|
+
pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
457
|
+
0x2492492492492492ULL));
|
|
458
|
+
#else
|
|
459
|
+
pos[0] = islet_sign((uint16_t)islet_compact_axis(code, 0));
|
|
460
|
+
pos[1] = islet_sign((uint16_t)islet_compact_axis(code, 1));
|
|
461
|
+
#endif
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* @brief Decode a 3D Morton code. See morton_get_1() for the family docs.
|
|
466
|
+
*/
|
|
467
|
+
static inline void
|
|
468
|
+
morton_get_3(int16_t *pos, uint64_t code)
|
|
469
|
+
{
|
|
470
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
471
|
+
pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
472
|
+
0x1249249249249249ULL));
|
|
473
|
+
pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
474
|
+
0x2492492492492492ULL));
|
|
475
|
+
pos[2] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
476
|
+
0x4924924924924924ULL));
|
|
477
|
+
#else
|
|
478
|
+
uint32_t uup[] = { 0, 0, 0 };
|
|
479
|
+
|
|
480
|
+
islet_decode3(code, &uup[0], &uup[1], &uup[2]);
|
|
481
|
+
pos[0] = islet_sign((uint16_t)uup[0]);
|
|
482
|
+
pos[1] = islet_sign((uint16_t)uup[1]);
|
|
483
|
+
pos[2] = islet_sign((uint16_t)uup[2]);
|
|
484
|
+
#endif
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @brief Decode a 4D Morton code. See morton_get_1() for the family docs.
|
|
489
|
+
*/
|
|
490
|
+
static inline void
|
|
491
|
+
morton_get_4(int16_t *pos, uint64_t code)
|
|
492
|
+
{
|
|
493
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
494
|
+
pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
495
|
+
0x1111111111111111ULL));
|
|
496
|
+
pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
497
|
+
0x2222222222222222ULL));
|
|
498
|
+
pos[2] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
499
|
+
0x4444444444444444ULL));
|
|
500
|
+
pos[3] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
|
|
501
|
+
0x8888888888888888ULL));
|
|
502
|
+
#else
|
|
503
|
+
uint32_t uup[] = { 0, 0, 0, 0 };
|
|
504
|
+
|
|
505
|
+
islet_decode4(code, &uup[0], &uup[1], &uup[2], &uup[3]);
|
|
506
|
+
pos[0] = islet_sign((uint16_t)uup[0]);
|
|
507
|
+
pos[1] = islet_sign((uint16_t)uup[1]);
|
|
508
|
+
pos[2] = islet_sign((uint16_t)uup[2]);
|
|
509
|
+
pos[3] = islet_sign((uint16_t)uup[3]);
|
|
510
|
+
#endif
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* @brief Decode a 2D x 32-bit Morton code. Inverse of morton_set_2_32().
|
|
515
|
+
*
|
|
516
|
+
* @param[out] pos Output point. Array of int32_t with space for
|
|
517
|
+
* 2 elements.
|
|
518
|
+
* @param[in] code Morton code to decode (64-bit, dense layout).
|
|
519
|
+
*/
|
|
520
|
+
static inline void
|
|
521
|
+
morton_get_2_32(int32_t *pos, uint64_t code)
|
|
522
|
+
{
|
|
523
|
+
#if ISLET_USE_PDEP && defined(__BMI2__)
|
|
524
|
+
pos[0] = islet_sign32((uint32_t)__builtin_ia32_pext_di(code,
|
|
525
|
+
0x5555555555555555ULL));
|
|
526
|
+
pos[1] = islet_sign32((uint32_t)__builtin_ia32_pext_di(code,
|
|
527
|
+
0xAAAAAAAAAAAAAAAAULL));
|
|
528
|
+
#else
|
|
529
|
+
pos[0] = islet_sign32((uint32_t)islet_compact_axis2(code, 0));
|
|
530
|
+
pos[1] = islet_sign32((uint32_t)islet_compact_axis2(code, 1));
|
|
531
|
+
#endif
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** @} */
|
|
535
|
+
|
|
536
|
+
#endif
|