@manycore/aholo-splat-transform 1.2.6

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.
Files changed (88) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +33 -0
  3. package/bin/cli.js +118 -0
  4. package/dist/SplatData.d.ts +67 -0
  5. package/dist/SplatData.js +156 -0
  6. package/dist/constant.d.ts +3 -0
  7. package/dist/constant.js +13 -0
  8. package/dist/file/IFile.d.ts +5 -0
  9. package/dist/file/IFile.js +1 -0
  10. package/dist/file/index.d.ts +7 -0
  11. package/dist/file/index.js +6 -0
  12. package/dist/file/ksplat.d.ts +12 -0
  13. package/dist/file/ksplat.js +232 -0
  14. package/dist/file/lcc.d.ts +11 -0
  15. package/dist/file/lcc.js +157 -0
  16. package/dist/file/ply.d.ts +13 -0
  17. package/dist/file/ply.js +388 -0
  18. package/dist/file/sog.d.ts +80 -0
  19. package/dist/file/sog.js +504 -0
  20. package/dist/file/splat.d.ts +6 -0
  21. package/dist/file/splat.js +99 -0
  22. package/dist/file/spz.d.ts +8 -0
  23. package/dist/file/spz.js +400 -0
  24. package/dist/file/voxel.d.ts +37 -0
  25. package/dist/file/voxel.js +280 -0
  26. package/dist/index.d.ts +33 -0
  27. package/dist/index.js +54 -0
  28. package/dist/native/cpp/bin/linux/binding.node +0 -0
  29. package/dist/native/cpp/bin/windows/binding.node +0 -0
  30. package/dist/native/index.d.ts +54 -0
  31. package/dist/native/index.js +128 -0
  32. package/dist/tasks/AutoChunkLodTask.d.ts +13 -0
  33. package/dist/tasks/AutoChunkLodTask.js +117 -0
  34. package/dist/tasks/AutoLodTask.d.ts +10 -0
  35. package/dist/tasks/AutoLodTask.js +20 -0
  36. package/dist/tasks/BaseTask.d.ts +15 -0
  37. package/dist/tasks/BaseTask.js +5 -0
  38. package/dist/tasks/FlexLodTask.d.ts +12 -0
  39. package/dist/tasks/FlexLodTask.js +44 -0
  40. package/dist/tasks/ModifyTask.d.ts +9 -0
  41. package/dist/tasks/ModifyTask.js +156 -0
  42. package/dist/tasks/ReadTask.d.ts +8 -0
  43. package/dist/tasks/ReadTask.js +29 -0
  44. package/dist/tasks/SkeletonLodTask.d.ts +10 -0
  45. package/dist/tasks/SkeletonLodTask.js +156 -0
  46. package/dist/tasks/VoxelTask.d.ts +30 -0
  47. package/dist/tasks/VoxelTask.js +37 -0
  48. package/dist/tasks/WriteTask.d.ts +11 -0
  49. package/dist/tasks/WriteTask.js +70 -0
  50. package/dist/utils/BufferReader.d.ts +12 -0
  51. package/dist/utils/BufferReader.js +47 -0
  52. package/dist/utils/Logger.d.ts +11 -0
  53. package/dist/utils/Logger.js +38 -0
  54. package/dist/utils/StreamChunkDecoder.d.ts +16 -0
  55. package/dist/utils/StreamChunkDecoder.js +36 -0
  56. package/dist/utils/index.d.ts +27 -0
  57. package/dist/utils/index.js +101 -0
  58. package/dist/utils/k-means.d.ts +4 -0
  59. package/dist/utils/k-means.js +350 -0
  60. package/dist/utils/math.d.ts +46 -0
  61. package/dist/utils/math.js +351 -0
  62. package/dist/utils/quantize-1d.d.ts +4 -0
  63. package/dist/utils/quantize-1d.js +164 -0
  64. package/dist/utils/sh-rotate.d.ts +2 -0
  65. package/dist/utils/sh-rotate.js +175 -0
  66. package/dist/utils/splat.d.ts +20 -0
  67. package/dist/utils/splat.js +378 -0
  68. package/dist/utils/voxel/common.d.ts +162 -0
  69. package/dist/utils/voxel/common.js +1700 -0
  70. package/dist/utils/voxel/coplanar-merge.d.ts +63 -0
  71. package/dist/utils/voxel/coplanar-merge.js +819 -0
  72. package/dist/utils/voxel/gpu-dilation.d.ts +2 -0
  73. package/dist/utils/voxel/gpu-dilation.js +665 -0
  74. package/dist/utils/voxel/marching-cubes.d.ts +42 -0
  75. package/dist/utils/voxel/marching-cubes.js +1657 -0
  76. package/dist/utils/voxel/mesh.d.ts +3 -0
  77. package/dist/utils/voxel/mesh.js +130 -0
  78. package/dist/utils/voxel/nav.d.ts +29 -0
  79. package/dist/utils/voxel/nav.js +1043 -0
  80. package/dist/utils/voxel/postprocess.d.ts +23 -0
  81. package/dist/utils/voxel/postprocess.js +375 -0
  82. package/dist/utils/voxel/voxel-faces.d.ts +18 -0
  83. package/dist/utils/voxel/voxel-faces.js +663 -0
  84. package/dist/utils/voxel/voxelize.d.ts +33 -0
  85. package/dist/utils/voxel/voxelize.js +1193 -0
  86. package/dist/utils/webgpu.d.ts +8 -0
  87. package/dist/utils/webgpu.js +122 -0
  88. package/package.json +32 -0
@@ -0,0 +1,1657 @@
1
+ import { BLOCKS_PER_WORD, EVEN_BITS, BLOCK_EMPTY, BLOCK_SOLID, readBlockType } from './common.js';
2
+ // ============================================================================
3
+ // Voxel bit helpers
4
+ // ============================================================================
5
+ // Bit layout within a 4x4x4 block: bitIdx = lx + ly*4 + lz*16
6
+ // lo = bits 0-31 (lz 0-1), hi = bits 32-63 (lz 2-3)
7
+ /**
8
+ * Test whether a voxel is occupied within a block's bitmask.
9
+ *
10
+ * @param lo - Lower 32 bits of the block mask
11
+ * @param hi - Upper 32 bits of the block mask
12
+ * @param lx - Local x coordinate (0-3)
13
+ * @param ly - Local y coordinate (0-3)
14
+ * @param lz - Local z coordinate (0-3)
15
+ * @returns True if the voxel is occupied
16
+ */
17
+ function isVoxelSet(lo, hi, lx, ly, lz) {
18
+ const bitIdx = lx + ly * 4 + lz * 16;
19
+ if (bitIdx < 32) {
20
+ return (lo & (1 << bitIdx)) !== 0;
21
+ }
22
+ return (hi & (1 << (bitIdx - 32))) !== 0;
23
+ }
24
+ // ============================================================================
25
+ // Marching Cubes
26
+ // ============================================================================
27
+ // Sentinel values for the per-block 3x3x3 neighbor table.
28
+ const NEIGHBOR_EMPTY = -2;
29
+ const NEIGHBOR_SOLID = -1;
30
+ /**
31
+ * Extract a triangle mesh from a SparseVoxelGrid using marching cubes.
32
+ *
33
+ * Each voxel is treated as a cell in the marching cubes grid. Corner values
34
+ * are binary (0 = empty, 1 = occupied) with a 0.5 threshold. Vertices are
35
+ * placed at edge midpoints, producing the binary-field isosurface between
36
+ * occupied and empty samples.
37
+ *
38
+ * @param grid - Voxel grid (after filtering / nav phases)
39
+ * @param gridBounds - Grid bounds aligned to block boundaries
40
+ * @param voxelResolution - Size of each voxel in world units
41
+ * @param options - Optional extraction settings
42
+ * @returns Mesh with positions and indices
43
+ */
44
+ function marchingCubes(grid, gridBounds, voxelResolution, options = {}) {
45
+ const { nbx, nby, nbz, bStride, types, masks } = grid;
46
+ const totalBlocks = nbx * nby * nbz;
47
+ const mergeFlatFaces = options.mergeFlatFaces === true;
48
+ // Vertex deduplication: edge ID -> vertex index. Open-addressed typed-
49
+ // array hash table rather than `Map<number, number>` because (1) a single
50
+ // V8 Map is capped at ~2^24 entries (large carved scenes blow past this),
51
+ // and (2) per-entry overhead in Map is ~50 bytes vs 12 bytes (Float64 key
52
+ // + Uint32 value) here, which matters when the table holds tens of
53
+ // millions of vertices.
54
+ //
55
+ // Empty slots are marked with `key === -1` (real keys are non-negative).
56
+ // Hash uses Fibonacci constant on the lower 32 bits of the key. The same
57
+ // structure is used for orphan-cell deduplication, where `vVals` is unused.
58
+ let vCap = 1 << 14;
59
+ let vMask = vCap - 1;
60
+ let vSize = 0;
61
+ let vKeys = new Float64Array(vCap).fill(-1);
62
+ let vVals = new Uint32Array(vCap);
63
+ const vGrow = () => {
64
+ const oldKeys = vKeys;
65
+ const oldVals = vVals;
66
+ const oldCap = vCap;
67
+ vCap *= 2;
68
+ vMask = vCap - 1;
69
+ vKeys = new Float64Array(vCap).fill(-1);
70
+ vVals = new Uint32Array(vCap);
71
+ for (let j = 0; j < oldCap; j++) {
72
+ const k = oldKeys[j];
73
+ if (k === -1) {
74
+ continue;
75
+ }
76
+ let i = (Math.imul(k | 0, 0x9E3779B9) >>> 0) & vMask;
77
+ while (vKeys[i] !== -1) {
78
+ i = (i + 1) & vMask;
79
+ }
80
+ vKeys[i] = k;
81
+ vVals[i] = oldVals[j];
82
+ }
83
+ };
84
+ let oCap = 1 << 14;
85
+ let oMask = oCap - 1;
86
+ let oSize = 0;
87
+ let oKeys = new Float64Array(oCap).fill(-1);
88
+ const oGrow = () => {
89
+ const oldKeys = oKeys;
90
+ const oldCap = oCap;
91
+ oCap *= 2;
92
+ oMask = oCap - 1;
93
+ oKeys = new Float64Array(oCap).fill(-1);
94
+ for (let j = 0; j < oldCap; j++) {
95
+ const k = oldKeys[j];
96
+ if (k === -1) {
97
+ continue;
98
+ }
99
+ let i = (Math.imul(k | 0, 0x9E3779B9) >>> 0) & oMask;
100
+ while (oKeys[i] !== -1) {
101
+ i = (i + 1) & oMask;
102
+ }
103
+ oKeys[i] = k;
104
+ }
105
+ };
106
+ // Growable typed-array buffers. Capacity doubles on demand to avoid
107
+ // the GC churn of pushing into JS number[] for huge meshes.
108
+ let posCap = 1024;
109
+ let posLen = 0;
110
+ let positions = new Float32Array(posCap);
111
+ let idxCap = 1024;
112
+ let idxLen = 0;
113
+ let indices = new Uint32Array(idxCap);
114
+ const originX = gridBounds.min.x;
115
+ const originY = gridBounds.min.y;
116
+ const originZ = gridBounds.min.z;
117
+ const gridNx = Math.round((gridBounds.max.x - gridBounds.min.x) / voxelResolution);
118
+ const gridNy = Math.round((gridBounds.max.y - gridBounds.min.y) / voxelResolution);
119
+ const gridNz = Math.round((gridBounds.max.z - gridBounds.min.z) / voxelResolution);
120
+ // Compute strides from actual grid dimensions (+3 for the -1 boundary
121
+ // extension, the far edge +1, and one extra for safety).
122
+ const strideX = gridNx + 3;
123
+ const strideXY = strideX * (gridNy + 3);
124
+ const scaledCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 2 + 5;
125
+ const scaledCoordOffset = 3;
126
+ // Per-block 3x3x3 neighbor lookup table populated once per processed block.
127
+ // Index = (dx+1) + (dy+1)*3 + (dz+1)*9, dx/dy/dz in {-1, 0, 1}.
128
+ // neighborEntry: NEIGHBOR_EMPTY, NEIGHBOR_SOLID, or NEIGHBOR_MIXED (mask in neighborMasks).
129
+ const NEIGHBOR_MIXED = 0;
130
+ const neighborEntry = new Int32Array(27);
131
+ const neighborMasks = new Uint32Array(54);
132
+ // Reused scratch for emitted edge vertex indices.
133
+ const edgeVerts = new Int32Array(12);
134
+ // Block coordinate of the block currently being processed. Captured by
135
+ // isOccupiedLocal so it can fold the per-corner block lookup into a
136
+ // direct typed-array index instead of a hash lookup.
137
+ let bx = 0, by = 0, bz = 0;
138
+ const isOccupiedLocal = (cx, cy, cz) => {
139
+ if (cx < 0 || cy < 0 || cz < 0) {
140
+ return false;
141
+ }
142
+ const idx = ((cx >> 2) - bx + 1) + ((cy >> 2) - by + 1) * 3 + ((cz >> 2) - bz + 1) * 9;
143
+ const entry = neighborEntry[idx];
144
+ if (entry === NEIGHBOR_EMPTY) {
145
+ return false;
146
+ }
147
+ if (entry === NEIGHBOR_SOLID) {
148
+ return true;
149
+ }
150
+ const lo = neighborMasks[idx * 2];
151
+ const hi = neighborMasks[idx * 2 + 1];
152
+ return isVoxelSet(lo, hi, cx & 3, cy & 3, cz & 3);
153
+ };
154
+ const addPosition = (px, py, pz) => {
155
+ if (posLen + 3 > posCap) {
156
+ posCap *= 2;
157
+ const grown = new Float32Array(posCap);
158
+ grown.set(positions);
159
+ positions = grown;
160
+ }
161
+ const idx = posLen / 3;
162
+ positions[posLen++] = px;
163
+ positions[posLen++] = py;
164
+ positions[posLen++] = pz;
165
+ return idx;
166
+ };
167
+ const ensureIndexCapacity = (additional) => {
168
+ if (idxLen + additional <= idxCap) {
169
+ return;
170
+ }
171
+ while (idxLen + additional > idxCap) {
172
+ idxCap *= 2;
173
+ }
174
+ const grown = new Uint32Array(idxCap);
175
+ grown.set(indices);
176
+ indices = grown;
177
+ };
178
+ const appendTri = (a, b, c) => {
179
+ ensureIndexCapacity(3);
180
+ indices[idxLen++] = a;
181
+ indices[idxLen++] = b;
182
+ indices[idxLen++] = c;
183
+ };
184
+ // When flat MC face cells are merged into large rectangles, rectangle
185
+ // boundaries must still be split at any neighbouring raw-MC vertex that
186
+ // lies along the same edge. Otherwise the pre-merged mesh can contain
187
+ // T-junctions before the final coplanarMerge pass. Coordinates here use
188
+ // the exact binary-MC half-grid: voxel corners are even, MC edge
189
+ // midpoints are odd on the crossed edge axis.
190
+ const splitLinePoints = mergeFlatFaces ? new Map() : undefined;
191
+ let collectSplitPoints = mergeFlatFaces;
192
+ const splitLineKey = (varAxis, x2, y2, z2) => {
193
+ const x = x2 + scaledCoordOffset;
194
+ const y = y2 + scaledCoordOffset;
195
+ const z = z2 + scaledCoordOffset;
196
+ if (varAxis === 0) {
197
+ return (y * scaledCoordStride + z) * 3;
198
+ }
199
+ if (varAxis === 1) {
200
+ return (x * scaledCoordStride + z) * 3 + 1;
201
+ }
202
+ return (x * scaledCoordStride + y) * 3 + 2;
203
+ };
204
+ const addSplitLinePoint = (key, value) => {
205
+ if (!splitLinePoints) {
206
+ return;
207
+ }
208
+ let points = splitLinePoints.get(key);
209
+ if (!points) {
210
+ points = [];
211
+ splitLinePoints.set(key, points);
212
+ }
213
+ points.push(value);
214
+ };
215
+ const addSplitPointForVertex = (vx, vy, vz, axis) => {
216
+ if (!splitLinePoints || !collectSplitPoints) {
217
+ return;
218
+ }
219
+ const x2 = vx * 2 + (axis === 0 ? 1 : 0);
220
+ const y2 = vy * 2 + (axis === 1 ? 1 : 0);
221
+ const z2 = vz * 2 + (axis === 2 ? 1 : 0);
222
+ addSplitLinePoint(splitLineKey(0, x2, y2, z2), x2);
223
+ addSplitLinePoint(splitLineKey(1, x2, y2, z2), y2);
224
+ addSplitLinePoint(splitLineKey(2, x2, y2, z2), z2);
225
+ };
226
+ // Get or create a vertex at the midpoint of an edge.
227
+ // Edge is identified by the lower corner voxel coordinate and axis (0=x, 1=y, 2=z).
228
+ const getVertex = (vx, vy, vz, axis) => {
229
+ // Pack (vx, vy, vz, axis) into a single key. Offset by 1 so that
230
+ // vx = -1 (from the boundary extension) maps to 0, keeping keys non-negative.
231
+ const key = ((vx + 1) + (vy + 1) * strideX + (vz + 1) * strideXY) * 3 + axis;
232
+ // Probe for either the matching slot or the next empty one.
233
+ let i = (Math.imul(key | 0, 0x9E3779B9) >>> 0) & vMask;
234
+ while (true) {
235
+ const k = vKeys[i];
236
+ if (k === key) {
237
+ return vVals[i];
238
+ }
239
+ if (k === -1) {
240
+ break;
241
+ }
242
+ i = (i + 1) & vMask;
243
+ }
244
+ let px = originX + vx * voxelResolution;
245
+ let py = originY + vy * voxelResolution;
246
+ let pz = originZ + vz * voxelResolution;
247
+ // Place vertex at edge midpoint (binary field -> always at 0.5)
248
+ if (axis === 0) {
249
+ px += voxelResolution * 0.5;
250
+ }
251
+ else if (axis === 1) {
252
+ py += voxelResolution * 0.5;
253
+ }
254
+ else {
255
+ pz += voxelResolution * 0.5;
256
+ }
257
+ const idx = addPosition(px, py, pz);
258
+ addSplitPointForVertex(vx, vy, vz, axis);
259
+ vKeys[i] = key;
260
+ vVals[i] = idx;
261
+ vSize++;
262
+ if (vSize > ((vCap * 0.7) | 0)) {
263
+ vGrow();
264
+ }
265
+ return idx;
266
+ };
267
+ // Full-face MC cases can be merged before vertex creation. Encode each
268
+ // unit face cell as a sortable integer in Float64: bucket / plane / u / v,
269
+ // where bucket = axis*2 + positiveNormalBit and coordinates are offset by
270
+ // +1 to cover the -1 boundary extension.
271
+ const faceCoordStride = Math.max(gridNx, gridNy, gridNz) + 3;
272
+ let faceCellCap = 0;
273
+ let faceCellLen = 0;
274
+ let faceCellKeys = new Float64Array(0);
275
+ const diagCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 8 + 9;
276
+ const diagCoordOffset = Math.floor(diagCoordStride / 2);
277
+ let diagCellCap = 0;
278
+ let diagCellLen = 0;
279
+ let diagCellKeys = new Float64Array(0);
280
+ const addFaceCell = (bucket, p, u, v) => {
281
+ if (faceCellLen === faceCellCap) {
282
+ faceCellCap = faceCellCap === 0 ? 1024 : faceCellCap * 2;
283
+ const grown = new Float64Array(faceCellCap);
284
+ grown.set(faceCellKeys);
285
+ faceCellKeys = grown;
286
+ }
287
+ faceCellKeys[faceCellLen++] =
288
+ (((bucket * faceCoordStride + (p + 1)) * faceCoordStride + (u + 1)) *
289
+ faceCoordStride + (v + 1));
290
+ };
291
+ const addDiagCell = (bucket, plane, u, e) => {
292
+ if (diagCellLen === diagCellCap) {
293
+ diagCellCap = diagCellCap === 0 ? 1024 : diagCellCap * 2;
294
+ const grown = new Float64Array(diagCellCap);
295
+ grown.set(diagCellKeys);
296
+ diagCellKeys = grown;
297
+ }
298
+ diagCellKeys[diagCellLen++] =
299
+ (((bucket * diagCoordStride + (plane + diagCoordOffset)) * diagCoordStride +
300
+ (u + diagCoordOffset)) * diagCoordStride + (e + diagCoordOffset));
301
+ };
302
+ const collectFlatFace = (cubeIndex, vx, vy, vz) => {
303
+ if (!mergeFlatFaces) {
304
+ return false;
305
+ }
306
+ switch (cubeIndex) {
307
+ case 153: // low-X corners occupied, high-X corners empty => +X normal
308
+ addFaceCell(1, vx, vy, vz);
309
+ return true;
310
+ case 102: // high-X occupied => -X normal
311
+ addFaceCell(0, vx, vy, vz);
312
+ return true;
313
+ case 51: // low-Y occupied => +Y normal
314
+ addFaceCell(3, vy, vx, vz);
315
+ return true;
316
+ case 204: // high-Y occupied => -Y normal
317
+ addFaceCell(2, vy, vx, vz);
318
+ return true;
319
+ case 15: // low-Z occupied => +Z normal
320
+ addFaceCell(5, vz, vx, vy);
321
+ return true;
322
+ case 240: // high-Z occupied => -Z normal
323
+ addFaceCell(4, vz, vx, vy);
324
+ return true;
325
+ default:
326
+ return false;
327
+ }
328
+ };
329
+ const scaledFacePoint = (axis, p, u, v) => {
330
+ if (axis === 0) {
331
+ return [p * 2 + 1, u * 2, v * 2];
332
+ }
333
+ if (axis === 1) {
334
+ return [u * 2, p * 2 + 1, v * 2];
335
+ }
336
+ return [u * 2, v * 2, p * 2 + 1];
337
+ };
338
+ const diagBucket = (axisA, axisB, signA, signB) => {
339
+ const pair = axisA === 0 ? (axisB === 1 ? 0 : 1) : 2;
340
+ const signBits = (signA > 0 ? 1 : 0) | (signB > 0 ? 2 : 0);
341
+ return pair * 4 + signBits;
342
+ };
343
+ const decodeDiagBucket = (bucket) => {
344
+ const pair = (bucket / 4) | 0;
345
+ const signBits = bucket & 3;
346
+ const signA = (signBits & 1) !== 0 ? 1 : -1;
347
+ const signB = (signBits & 2) !== 0 ? 1 : -1;
348
+ if (pair === 0) {
349
+ return { axisA: 0, axisB: 1, axisE: 2, signA, signB };
350
+ }
351
+ if (pair === 1) {
352
+ return { axisA: 0, axisB: 2, axisE: 1, signA, signB };
353
+ }
354
+ return { axisA: 1, axisB: 2, axisE: 0, signA, signB };
355
+ };
356
+ const coordByAxis = (x, y, z, axis) => {
357
+ if (axis === 0) {
358
+ return x;
359
+ }
360
+ if (axis === 1) {
361
+ return y;
362
+ }
363
+ return z;
364
+ };
365
+ const diagPoint = (bucket, plane, u, e) => {
366
+ const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
367
+ const out = [0, 0, 0];
368
+ out[axisA] = signA * ((plane + u) / 2);
369
+ out[axisB] = signB * ((plane - u) / 2);
370
+ out[axisE] = e;
371
+ return [out[0], out[1], out[2]];
372
+ };
373
+ const edgeScaledPoint = (edge, vx, vy, vz, out, offset) => {
374
+ const x = vx * 2;
375
+ const y = vy * 2;
376
+ const z = vz * 2;
377
+ switch (edge) {
378
+ case 0:
379
+ out[offset] = x + 1;
380
+ out[offset + 1] = y;
381
+ out[offset + 2] = z;
382
+ break;
383
+ case 1:
384
+ out[offset] = x + 2;
385
+ out[offset + 1] = y + 1;
386
+ out[offset + 2] = z;
387
+ break;
388
+ case 2:
389
+ out[offset] = x + 1;
390
+ out[offset + 1] = y + 2;
391
+ out[offset + 2] = z;
392
+ break;
393
+ case 3:
394
+ out[offset] = x;
395
+ out[offset + 1] = y + 1;
396
+ out[offset + 2] = z;
397
+ break;
398
+ case 4:
399
+ out[offset] = x + 1;
400
+ out[offset + 1] = y;
401
+ out[offset + 2] = z + 2;
402
+ break;
403
+ case 5:
404
+ out[offset] = x + 2;
405
+ out[offset + 1] = y + 1;
406
+ out[offset + 2] = z + 2;
407
+ break;
408
+ case 6:
409
+ out[offset] = x + 1;
410
+ out[offset + 1] = y + 2;
411
+ out[offset + 2] = z + 2;
412
+ break;
413
+ case 7:
414
+ out[offset] = x;
415
+ out[offset + 1] = y + 1;
416
+ out[offset + 2] = z + 2;
417
+ break;
418
+ case 8:
419
+ out[offset] = x;
420
+ out[offset + 1] = y;
421
+ out[offset + 2] = z + 1;
422
+ break;
423
+ case 9:
424
+ out[offset] = x + 2;
425
+ out[offset + 1] = y;
426
+ out[offset + 2] = z + 1;
427
+ break;
428
+ case 10:
429
+ out[offset] = x + 2;
430
+ out[offset + 1] = y + 2;
431
+ out[offset + 2] = z + 1;
432
+ break;
433
+ default:
434
+ out[offset] = x;
435
+ out[offset + 1] = y + 2;
436
+ out[offset + 2] = z + 1;
437
+ break;
438
+ }
439
+ };
440
+ const pairVerts = new Int32Array(18);
441
+ const uniqueVerts = new Int32Array(12);
442
+ const samePoint = (src, a, b) => src[a] === src[b] && src[a + 1] === src[b + 1] && src[a + 2] === src[b + 2];
443
+ const pointInUnique = (x, y, z, uniqueCount) => {
444
+ for (let i = 0; i < uniqueCount; i++) {
445
+ const o = i * 3;
446
+ if (uniqueVerts[o] === x && uniqueVerts[o + 1] === y && uniqueVerts[o + 2] === z) {
447
+ return true;
448
+ }
449
+ }
450
+ return false;
451
+ };
452
+ const collectDiagPair = (triRow, triA, triB, vx, vy, vz) => {
453
+ const edgesA = triA * 3;
454
+ const edgesB = triB * 3;
455
+ edgeScaledPoint(triRow[edgesA], vx, vy, vz, pairVerts, 0);
456
+ edgeScaledPoint(triRow[edgesA + 2], vx, vy, vz, pairVerts, 3);
457
+ edgeScaledPoint(triRow[edgesA + 1], vx, vy, vz, pairVerts, 6);
458
+ edgeScaledPoint(triRow[edgesB], vx, vy, vz, pairVerts, 9);
459
+ edgeScaledPoint(triRow[edgesB + 2], vx, vy, vz, pairVerts, 12);
460
+ edgeScaledPoint(triRow[edgesB + 1], vx, vy, vz, pairVerts, 15);
461
+ let uniqueCount = 0;
462
+ for (let i = 0; i < 6; i++) {
463
+ const src = i * 3;
464
+ let found = false;
465
+ for (let j = 0; j < uniqueCount; j++) {
466
+ const dst = j * 3;
467
+ if (pairVerts[src] === uniqueVerts[dst] &&
468
+ pairVerts[src + 1] === uniqueVerts[dst + 1] &&
469
+ pairVerts[src + 2] === uniqueVerts[dst + 2]) {
470
+ found = true;
471
+ break;
472
+ }
473
+ }
474
+ if (!found) {
475
+ if (uniqueCount === 4) {
476
+ return false;
477
+ }
478
+ const dst = uniqueCount * 3;
479
+ uniqueVerts[dst] = pairVerts[src];
480
+ uniqueVerts[dst + 1] = pairVerts[src + 1];
481
+ uniqueVerts[dst + 2] = pairVerts[src + 2];
482
+ uniqueCount++;
483
+ }
484
+ }
485
+ if (uniqueCount !== 4) {
486
+ return false;
487
+ }
488
+ const ax = pairVerts[3] - pairVerts[0];
489
+ const ay = pairVerts[4] - pairVerts[1];
490
+ const az = pairVerts[5] - pairVerts[2];
491
+ const bx = pairVerts[6] - pairVerts[0];
492
+ const by = pairVerts[7] - pairVerts[1];
493
+ const bz = pairVerts[8] - pairVerts[2];
494
+ const normal = [
495
+ ay * bz - az * by,
496
+ az * bx - ax * bz,
497
+ ax * by - ay * bx
498
+ ];
499
+ const absNormal = [Math.abs(normal[0]), Math.abs(normal[1]), Math.abs(normal[2])];
500
+ let axisE = -1;
501
+ let axisA = -1;
502
+ let axisB = -1;
503
+ for (let i = 0; i < 3; i++) {
504
+ if (absNormal[i] === 0) {
505
+ axisE = i;
506
+ }
507
+ else if (axisA === -1) {
508
+ axisA = i;
509
+ }
510
+ else {
511
+ axisB = i;
512
+ }
513
+ }
514
+ if (axisE === -1 || axisA === -1 || axisB === -1) {
515
+ return false;
516
+ }
517
+ if (absNormal[axisA] !== absNormal[axisB]) {
518
+ return false;
519
+ }
520
+ const signA = normal[axisA] > 0 ? 1 : -1;
521
+ const signB = normal[axisB] > 0 ? 1 : -1;
522
+ const bucket = diagBucket(axisA, axisB, signA, signB);
523
+ const plane = signA * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisA) +
524
+ signB * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisB);
525
+ let minU = Infinity;
526
+ let maxU = -Infinity;
527
+ let minE = Infinity;
528
+ let maxE = -Infinity;
529
+ for (let i = 0; i < uniqueCount; i++) {
530
+ const o = i * 3;
531
+ const a = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisA);
532
+ const b = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisB);
533
+ const e = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisE);
534
+ if (signA * a + signB * b !== plane) {
535
+ return false;
536
+ }
537
+ const u = signA * a - signB * b;
538
+ if (u < minU) {
539
+ minU = u;
540
+ }
541
+ if (u > maxU) {
542
+ maxU = u;
543
+ }
544
+ if (e < minE) {
545
+ minE = e;
546
+ }
547
+ if (e > maxE) {
548
+ maxE = e;
549
+ }
550
+ }
551
+ if (maxU - minU !== 2 || maxE - minE !== 2) {
552
+ return false;
553
+ }
554
+ const p00 = diagPoint(bucket, plane, minU, minE);
555
+ const p10 = diagPoint(bucket, plane, maxU, minE);
556
+ const p11 = diagPoint(bucket, plane, maxU, maxE);
557
+ const p01 = diagPoint(bucket, plane, minU, maxE);
558
+ if (!pointInUnique(p00[0], p00[1], p00[2], uniqueCount) ||
559
+ !pointInUnique(p10[0], p10[1], p10[2], uniqueCount) ||
560
+ !pointInUnique(p11[0], p11[1], p11[2], uniqueCount) ||
561
+ !pointInUnique(p01[0], p01[1], p01[2], uniqueCount)) {
562
+ return false;
563
+ }
564
+ let sharedCount = 0;
565
+ let sharedU0 = 0;
566
+ let sharedE0 = 0;
567
+ let sharedU1 = 0;
568
+ let sharedE1 = 0;
569
+ for (let i = 0; i < 3; i++) {
570
+ const oi = i * 3;
571
+ for (let j = 3; j < 6; j++) {
572
+ const oj = j * 3;
573
+ if (!samePoint(pairVerts, oi, oj)) {
574
+ continue;
575
+ }
576
+ const a = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisA);
577
+ const b = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisB);
578
+ const e = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisE);
579
+ if (sharedCount === 0) {
580
+ sharedU0 = signA * a - signB * b;
581
+ sharedE0 = e;
582
+ }
583
+ else {
584
+ sharedU1 = signA * a - signB * b;
585
+ sharedE1 = e;
586
+ }
587
+ sharedCount++;
588
+ }
589
+ }
590
+ if (sharedCount !== 2 || sharedU0 === sharedU1 || sharedE0 === sharedE1) {
591
+ return false;
592
+ }
593
+ addDiagCell(bucket, plane, minU, minE);
594
+ return true;
595
+ };
596
+ const collectDiagFaces = (triRow, vx, vy, vz) => {
597
+ if (!mergeFlatFaces) {
598
+ return 0;
599
+ }
600
+ const triCount = (triRow.length / 3) | 0;
601
+ let usedMask = 0;
602
+ for (let i = 0; i < triCount; i++) {
603
+ if ((usedMask & (1 << i)) !== 0) {
604
+ continue;
605
+ }
606
+ for (let j = i + 1; j < triCount; j++) {
607
+ if ((usedMask & (1 << j)) !== 0) {
608
+ continue;
609
+ }
610
+ if (collectDiagPair(triRow, i, j, vx, vy, vz)) {
611
+ usedMask |= (1 << i) | (1 << j);
612
+ break;
613
+ }
614
+ }
615
+ }
616
+ return usedMask;
617
+ };
618
+ const getScaledVertex = (x2, y2, z2) => {
619
+ if ((x2 & 1) !== 0) {
620
+ return getVertex((x2 - 1) / 2, y2 / 2, z2 / 2, 0);
621
+ }
622
+ if ((y2 & 1) !== 0) {
623
+ return getVertex(x2 / 2, (y2 - 1) / 2, z2 / 2, 1);
624
+ }
625
+ return getVertex(x2 / 2, y2 / 2, (z2 - 1) / 2, 2);
626
+ };
627
+ let perimeterScratch = new Uint32Array(16);
628
+ let perimeterU = new Int32Array(16);
629
+ let perimeterV = new Int32Array(16);
630
+ let perimeterLen = 0;
631
+ const localFaceUv = (axis, x2, y2, z2) => {
632
+ if (axis === 0) {
633
+ return [y2, z2];
634
+ }
635
+ if (axis === 1) {
636
+ return [x2, z2];
637
+ }
638
+ return [x2, y2];
639
+ };
640
+ const addPerimeterVertex = (vertex, u, v) => {
641
+ if (perimeterLen > 0 && perimeterScratch[perimeterLen - 1] === vertex) {
642
+ return;
643
+ }
644
+ if (perimeterLen === perimeterScratch.length) {
645
+ const grown = new Uint32Array(perimeterScratch.length * 2);
646
+ grown.set(perimeterScratch);
647
+ perimeterScratch = grown;
648
+ const grownU = new Int32Array(perimeterU.length * 2);
649
+ grownU.set(perimeterU);
650
+ perimeterU = grownU;
651
+ const grownV = new Int32Array(perimeterV.length * 2);
652
+ grownV.set(perimeterV);
653
+ perimeterV = grownV;
654
+ }
655
+ perimeterScratch[perimeterLen] = vertex;
656
+ perimeterU[perimeterLen] = u;
657
+ perimeterV[perimeterLen] = v;
658
+ perimeterLen++;
659
+ };
660
+ const addPerimeterPoint = (axis, x2, y2, z2) => {
661
+ const [u, v] = localFaceUv(axis, x2, y2, z2);
662
+ addPerimeterVertex(getScaledVertex(x2, y2, z2), u, v);
663
+ };
664
+ const addDiagPerimeterPoint = (bucket, x2, y2, z2) => {
665
+ const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
666
+ const a = coordByAxis(x2, y2, z2, axisA);
667
+ const b = coordByAxis(x2, y2, z2, axisB);
668
+ const e = coordByAxis(x2, y2, z2, axisE);
669
+ addPerimeterVertex(getScaledVertex(x2, y2, z2), signA * a - signB * b, e);
670
+ };
671
+ const addSplitSegment = (x0, y0, z0, x1, y1, z1) => {
672
+ const changes = (x0 !== x1 ? 1 : 0) + (y0 !== y1 ? 1 : 0) + (z0 !== z1 ? 1 : 0);
673
+ if (changes !== 1) {
674
+ return;
675
+ }
676
+ if (x0 !== x1) {
677
+ const key = splitLineKey(0, x0, y0, z0);
678
+ addSplitLinePoint(key, x0);
679
+ addSplitLinePoint(key, x1);
680
+ }
681
+ else if (y0 !== y1) {
682
+ const key = splitLineKey(1, x0, y0, z0);
683
+ addSplitLinePoint(key, y0);
684
+ addSplitLinePoint(key, y1);
685
+ }
686
+ else {
687
+ const key = splitLineKey(2, x0, y0, z0);
688
+ addSplitLinePoint(key, z0);
689
+ addSplitLinePoint(key, z1);
690
+ }
691
+ };
692
+ const addSplitEdgeVertices = (axis, x0, y0, z0, x1, y1, z1, addPoint = (px, py, pz) => {
693
+ addPerimeterPoint(axis, px, py, pz);
694
+ }) => {
695
+ let varAxis;
696
+ let start;
697
+ let end;
698
+ if (x0 !== x1) {
699
+ varAxis = 0;
700
+ start = x0;
701
+ end = x1;
702
+ }
703
+ else if (y0 !== y1) {
704
+ varAxis = 1;
705
+ start = y0;
706
+ end = y1;
707
+ }
708
+ else {
709
+ varAxis = 2;
710
+ start = z0;
711
+ end = z1;
712
+ }
713
+ const points = splitLinePoints?.get(splitLineKey(varAxis, x0, y0, z0));
714
+ if (!points) {
715
+ return;
716
+ }
717
+ const lo = Math.min(start, end);
718
+ const hi = Math.max(start, end);
719
+ const forward = start <= end;
720
+ const emitPoint = (t) => {
721
+ if (varAxis === 0) {
722
+ addPoint(t, y0, z0);
723
+ }
724
+ else if (varAxis === 1) {
725
+ addPoint(x0, t, z0);
726
+ }
727
+ else {
728
+ addPoint(x0, y0, t);
729
+ }
730
+ };
731
+ if (forward) {
732
+ for (let i = 0; i < points.length; i++) {
733
+ const t = points[i];
734
+ if (t < lo) {
735
+ continue;
736
+ }
737
+ if (t > hi) {
738
+ break;
739
+ }
740
+ emitPoint(t);
741
+ }
742
+ }
743
+ else {
744
+ for (let i = points.length - 1; i >= 0; i--) {
745
+ const t = points[i];
746
+ if (t > hi) {
747
+ continue;
748
+ }
749
+ if (t < lo) {
750
+ break;
751
+ }
752
+ emitPoint(t);
753
+ }
754
+ }
755
+ };
756
+ const appendOrientedTri = (a, b, c, useLocalCcw) => {
757
+ if (useLocalCcw) {
758
+ appendTri(a, b, c);
759
+ }
760
+ else {
761
+ appendTri(a, c, b);
762
+ }
763
+ };
764
+ const appendPerimeterTri = (a, b, c, useLocalCcw) => {
765
+ const abx = perimeterU[b] - perimeterU[a];
766
+ const aby = perimeterV[b] - perimeterV[a];
767
+ const acx = perimeterU[c] - perimeterU[a];
768
+ const acy = perimeterV[c] - perimeterV[a];
769
+ if (abx * acy - aby * acx <= 0) {
770
+ return;
771
+ }
772
+ appendOrientedTri(perimeterScratch[a], perimeterScratch[b], perimeterScratch[c], useLocalCcw);
773
+ };
774
+ const triangulateTwoSideChain = (chainAStart, chainAEnd, chainBStart, chainBEnd, useLocalCcw) => {
775
+ const chainALen = chainAEnd - chainAStart;
776
+ const chainBLen = chainBEnd - chainBStart;
777
+ if (chainALen < 2 || chainBLen < 2) {
778
+ return;
779
+ }
780
+ const v2 = chainBEnd - 1;
781
+ for (let i = chainAStart; i < chainAEnd - 2; i++) {
782
+ appendPerimeterTri(v2, i, i + 1, useLocalCcw);
783
+ }
784
+ const pivot = chainALen > 1 ? chainAEnd - 2 : chainAStart;
785
+ for (let i = chainBStart; i < chainBEnd - 1; i++) {
786
+ appendPerimeterTri(pivot, i, i + 1, useLocalCcw);
787
+ }
788
+ };
789
+ const emitFaceRectangle = (bucket, p, u0, v0, u1, v1) => {
790
+ const axis = bucket >> 1;
791
+ const positive = (bucket & 1) === 1;
792
+ const a = scaledFacePoint(axis, p, u0, v0);
793
+ const b = scaledFacePoint(axis, p, u1, v0);
794
+ const c = scaledFacePoint(axis, p, u1, v1);
795
+ const d = scaledFacePoint(axis, p, u0, v1);
796
+ perimeterLen = 0;
797
+ const side0Start = 0;
798
+ addSplitEdgeVertices(axis, a[0], a[1], a[2], b[0], b[1], b[2]);
799
+ const side0End = perimeterLen;
800
+ const side1Start = side0End - 1;
801
+ addSplitEdgeVertices(axis, b[0], b[1], b[2], c[0], c[1], c[2]);
802
+ const side1End = perimeterLen;
803
+ const side2Start = side1End - 1;
804
+ addSplitEdgeVertices(axis, c[0], c[1], c[2], d[0], d[1], d[2]);
805
+ const side2End = perimeterLen;
806
+ const side3Start = side2End - 1;
807
+ addSplitEdgeVertices(axis, d[0], d[1], d[2], a[0], a[1], a[2]);
808
+ const side3End = perimeterLen;
809
+ const localCcwIsPositive = axis !== 1;
810
+ const useLocalCcw = positive === localCcwIsPositive;
811
+ triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
812
+ triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
813
+ };
814
+ const emitDiagRectangle = (bucket, plane, u0, e0, u1, e1) => {
815
+ const a = diagPoint(bucket, plane, u0, e0);
816
+ const b = diagPoint(bucket, plane, u1, e0);
817
+ const c = diagPoint(bucket, plane, u1, e1);
818
+ const d = diagPoint(bucket, plane, u0, e1);
819
+ const addPoint = (x2, y2, z2) => {
820
+ addDiagPerimeterPoint(bucket, x2, y2, z2);
821
+ };
822
+ perimeterLen = 0;
823
+ const side0Start = 0;
824
+ addPoint(a[0], a[1], a[2]);
825
+ addPoint(b[0], b[1], b[2]);
826
+ const side0End = perimeterLen;
827
+ const side1Start = side0End - 1;
828
+ addSplitEdgeVertices(0, b[0], b[1], b[2], c[0], c[1], c[2], addPoint);
829
+ const side1End = perimeterLen;
830
+ const side2Start = side1End - 1;
831
+ addPoint(d[0], d[1], d[2]);
832
+ const side2End = perimeterLen;
833
+ const side3Start = side2End - 1;
834
+ addSplitEdgeVertices(0, d[0], d[1], d[2], a[0], a[1], a[2], addPoint);
835
+ const side3End = perimeterLen;
836
+ const { axisA, axisB, signA, signB } = decodeDiagBucket(bucket);
837
+ const abx = b[0] - a[0];
838
+ const aby = b[1] - a[1];
839
+ const abz = b[2] - a[2];
840
+ const bcx = c[0] - b[0];
841
+ const bcy = c[1] - b[1];
842
+ const bcz = c[2] - b[2];
843
+ const nx = aby * bcz - abz * bcy;
844
+ const ny = abz * bcx - abx * bcz;
845
+ const nz = abx * bcy - aby * bcx;
846
+ const normal = [0, 0, 0];
847
+ normal[axisA] = signA;
848
+ normal[axisB] = signB;
849
+ const useLocalCcw = nx * normal[0] + ny * normal[1] + nz * normal[2] > 0;
850
+ triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
851
+ triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
852
+ };
853
+ const flushFaceCells = () => {
854
+ if (faceCellLen === 0 && diagCellLen === 0) {
855
+ return;
856
+ }
857
+ const keys = faceCellKeys.subarray(0, faceCellLen);
858
+ faceCellKeys = new Float64Array(0);
859
+ keys.sort();
860
+ let rectCap = 1024;
861
+ let rectLen = 0;
862
+ let rectBucket = new Int32Array(rectCap);
863
+ let rectP = new Int32Array(rectCap);
864
+ let rectU0 = new Int32Array(rectCap);
865
+ let rectV0 = new Int32Array(rectCap);
866
+ let rectU1 = new Int32Array(rectCap);
867
+ let rectV1 = new Int32Array(rectCap);
868
+ let diagRectCap = 1024;
869
+ let diagRectLen = 0;
870
+ let diagRectBucket = new Int32Array(diagRectCap);
871
+ let diagRectPlane = new Int32Array(diagRectCap);
872
+ let diagRectU0 = new Int32Array(diagRectCap);
873
+ let diagRectE0 = new Int32Array(diagRectCap);
874
+ let diagRectU1 = new Int32Array(diagRectCap);
875
+ let diagRectE1 = new Int32Array(diagRectCap);
876
+ const addRect = (bucket, p, u0, v0, u1, v1) => {
877
+ if (rectLen === rectCap) {
878
+ rectCap *= 2;
879
+ const grow = (src) => {
880
+ const out = new Int32Array(rectCap);
881
+ out.set(src);
882
+ return out;
883
+ };
884
+ rectBucket = grow(rectBucket);
885
+ rectP = grow(rectP);
886
+ rectU0 = grow(rectU0);
887
+ rectV0 = grow(rectV0);
888
+ rectU1 = grow(rectU1);
889
+ rectV1 = grow(rectV1);
890
+ }
891
+ rectBucket[rectLen] = bucket;
892
+ rectP[rectLen] = p;
893
+ rectU0[rectLen] = u0;
894
+ rectV0[rectLen] = v0;
895
+ rectU1[rectLen] = u1;
896
+ rectV1[rectLen] = v1;
897
+ rectLen++;
898
+ };
899
+ const addDiagRect = (bucket, plane, u0, e0, u1, e1) => {
900
+ if (diagRectLen === diagRectCap) {
901
+ diagRectCap *= 2;
902
+ const grow = (src) => {
903
+ const out = new Int32Array(diagRectCap);
904
+ out.set(src);
905
+ return out;
906
+ };
907
+ diagRectBucket = grow(diagRectBucket);
908
+ diagRectPlane = grow(diagRectPlane);
909
+ diagRectU0 = grow(diagRectU0);
910
+ diagRectE0 = grow(diagRectE0);
911
+ diagRectU1 = grow(diagRectU1);
912
+ diagRectE1 = grow(diagRectE1);
913
+ }
914
+ diagRectBucket[diagRectLen] = bucket;
915
+ diagRectPlane[diagRectLen] = plane;
916
+ diagRectU0[diagRectLen] = u0;
917
+ diagRectE0[diagRectLen] = e0;
918
+ diagRectU1[diagRectLen] = u1;
919
+ diagRectE1[diagRectLen] = e1;
920
+ diagRectLen++;
921
+ };
922
+ const decodeGroup = (key) => {
923
+ let q = Math.floor(key / faceCoordStride);
924
+ q = Math.floor(q / faceCoordStride);
925
+ const pOff = q % faceCoordStride;
926
+ const bucket = Math.floor(q / faceCoordStride);
927
+ return { bucket, pOff };
928
+ };
929
+ const decodeUvKey = (key) => {
930
+ const vOff = key % faceCoordStride;
931
+ const q = Math.floor(key / faceCoordStride);
932
+ const uOff = q % faceCoordStride;
933
+ return uOff * faceCoordStride + vOff;
934
+ };
935
+ let start = 0;
936
+ while (start < keys.length) {
937
+ const { bucket, pOff } = decodeGroup(keys[start]);
938
+ let end = start + 1;
939
+ while (end < keys.length) {
940
+ const g = decodeGroup(keys[end]);
941
+ if (g.bucket !== bucket || g.pOff !== pOff) {
942
+ break;
943
+ }
944
+ end++;
945
+ }
946
+ const count = end - start;
947
+ let hCap = 1;
948
+ while (hCap < count / 0.7) {
949
+ hCap *= 2;
950
+ }
951
+ const hMask = hCap - 1;
952
+ const hKeys = new Float64Array(hCap).fill(-1);
953
+ const hVals = new Int32Array(hCap);
954
+ const hash = (key) => {
955
+ const hi = (key / 0x100000000) | 0;
956
+ return (Math.imul((key | 0) ^ hi, 0x9E3779B9) >>> 0) & hMask;
957
+ };
958
+ for (let i = 0; i < count; i++) {
959
+ const uvKey = decodeUvKey(keys[start + i]);
960
+ let h = hash(uvKey);
961
+ while (hKeys[h] !== -1) {
962
+ h = (h + 1) & hMask;
963
+ }
964
+ hKeys[h] = uvKey;
965
+ hVals[h] = i;
966
+ }
967
+ const lookup = (uvKey) => {
968
+ let h = hash(uvKey);
969
+ while (true) {
970
+ const k = hKeys[h];
971
+ if (k === uvKey) {
972
+ return hVals[h];
973
+ }
974
+ if (k === -1) {
975
+ return -1;
976
+ }
977
+ h = (h + 1) & hMask;
978
+ }
979
+ };
980
+ const visited = new Uint8Array(count);
981
+ const uvKeyOf = (uOff, vOff) => uOff * faceCoordStride + vOff;
982
+ const p = pOff - 1;
983
+ for (let i = 0; i < count; i++) {
984
+ if (visited[i]) {
985
+ continue;
986
+ }
987
+ const uvKey = decodeUvKey(keys[start + i]);
988
+ const uOff = Math.floor(uvKey / faceCoordStride);
989
+ const vOff = uvKey % faceCoordStride;
990
+ let width = 1;
991
+ while (true) {
992
+ const idx = lookup(uvKeyOf(uOff + width, vOff));
993
+ if (idx === -1 || visited[idx]) {
994
+ break;
995
+ }
996
+ width++;
997
+ }
998
+ let height = 1;
999
+ while (true) {
1000
+ let canGrow = true;
1001
+ for (let du = 0; du < width; du++) {
1002
+ const idx = lookup(uvKeyOf(uOff + du, vOff + height));
1003
+ if (idx === -1 || visited[idx]) {
1004
+ canGrow = false;
1005
+ break;
1006
+ }
1007
+ }
1008
+ if (!canGrow) {
1009
+ break;
1010
+ }
1011
+ height++;
1012
+ }
1013
+ for (let dv = 0; dv < height; dv++) {
1014
+ for (let du = 0; du < width; du++) {
1015
+ const idx = lookup(uvKeyOf(uOff + du, vOff + dv));
1016
+ visited[idx] = 1;
1017
+ }
1018
+ }
1019
+ addRect(bucket, p, uOff - 1, vOff - 1, uOff - 1 + width, vOff - 1 + height);
1020
+ }
1021
+ start = end;
1022
+ }
1023
+ if (diagCellLen > 0) {
1024
+ const diagKeys = diagCellKeys.slice(0, diagCellLen);
1025
+ diagCellKeys = new Float64Array(0);
1026
+ diagKeys.sort();
1027
+ const decodeDiagKey = (key) => {
1028
+ const eOff = key % diagCoordStride;
1029
+ let q = Math.floor(key / diagCoordStride);
1030
+ const uOff = q % diagCoordStride;
1031
+ q = Math.floor(q / diagCoordStride);
1032
+ const planeOff = q % diagCoordStride;
1033
+ const bucket = Math.floor(q / diagCoordStride);
1034
+ return {
1035
+ bucket,
1036
+ plane: planeOff - diagCoordOffset,
1037
+ u: uOff - diagCoordOffset,
1038
+ e: eOff - diagCoordOffset
1039
+ };
1040
+ };
1041
+ let diagStart = 0;
1042
+ while (diagStart < diagKeys.length) {
1043
+ const first = decodeDiagKey(diagKeys[diagStart]);
1044
+ let diagEnd = diagStart + 1;
1045
+ while (diagEnd < diagKeys.length) {
1046
+ const next = decodeDiagKey(diagKeys[diagEnd]);
1047
+ if (next.bucket !== first.bucket || next.plane !== first.plane || next.u !== first.u) {
1048
+ break;
1049
+ }
1050
+ diagEnd++;
1051
+ }
1052
+ let i = diagStart;
1053
+ while (i < diagEnd) {
1054
+ const run = decodeDiagKey(diagKeys[i]);
1055
+ let e1 = run.e + 2;
1056
+ i++;
1057
+ while (i < diagEnd) {
1058
+ const next = decodeDiagKey(diagKeys[i]);
1059
+ if (next.e !== e1) {
1060
+ break;
1061
+ }
1062
+ e1 += 2;
1063
+ i++;
1064
+ }
1065
+ addDiagRect(run.bucket, run.plane, run.u, run.e, run.u + 2, e1);
1066
+ }
1067
+ diagStart = diagEnd;
1068
+ }
1069
+ }
1070
+ for (let r = 0; r < rectLen; r++) {
1071
+ const axis = rectBucket[r] >> 1;
1072
+ const p = rectP[r];
1073
+ const u0 = rectU0[r];
1074
+ const v0 = rectV0[r];
1075
+ const u1 = rectU1[r];
1076
+ const v1 = rectV1[r];
1077
+ const a = scaledFacePoint(axis, p, u0, v0);
1078
+ const b = scaledFacePoint(axis, p, u1, v0);
1079
+ const c = scaledFacePoint(axis, p, u1, v1);
1080
+ const d = scaledFacePoint(axis, p, u0, v1);
1081
+ addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
1082
+ addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
1083
+ addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
1084
+ addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
1085
+ }
1086
+ for (let r = 0; r < diagRectLen; r++) {
1087
+ const bucket = diagRectBucket[r];
1088
+ const plane = diagRectPlane[r];
1089
+ const u0 = diagRectU0[r];
1090
+ const e0 = diagRectE0[r];
1091
+ const u1 = diagRectU1[r];
1092
+ const e1 = diagRectE1[r];
1093
+ const a = diagPoint(bucket, plane, u0, e0);
1094
+ const b = diagPoint(bucket, plane, u1, e0);
1095
+ const c = diagPoint(bucket, plane, u1, e1);
1096
+ const d = diagPoint(bucket, plane, u0, e1);
1097
+ addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
1098
+ addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
1099
+ addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
1100
+ addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
1101
+ }
1102
+ if (splitLinePoints) {
1103
+ for (const points of splitLinePoints.values()) {
1104
+ points.sort((a, b) => a - b);
1105
+ let write = 0;
1106
+ for (let i = 0; i < points.length; i++) {
1107
+ if (i === 0 || points[i] !== points[i - 1]) {
1108
+ points[write++] = points[i];
1109
+ }
1110
+ }
1111
+ points.length = write;
1112
+ }
1113
+ }
1114
+ collectSplitPoints = false;
1115
+ for (let r = 0; r < rectLen; r++) {
1116
+ emitFaceRectangle(rectBucket[r], rectP[r], rectU0[r], rectV0[r], rectU1[r], rectV1[r]);
1117
+ }
1118
+ for (let r = 0; r < diagRectLen; r++) {
1119
+ emitDiagRectangle(diagRectBucket[r], diagRectPlane[r], diagRectU0[r], diagRectE0[r], diagRectU1[r], diagRectE1[r]);
1120
+ }
1121
+ splitLinePoints?.clear();
1122
+ };
1123
+ // Track processed orphan cells to avoid duplicate triangles. When a cell's
1124
+ // owner block doesn't exist, multiple neighboring blocks can reach it via
1125
+ // the -1 boundary extension. The hash table ensures each orphan cell is
1126
+ // only processed once. Same typed-array structure as the vertex hash —
1127
+ // see `oKeys` / `oGrow` above. Stored separately because keys collide with
1128
+ // vertex keys (same encoding, different namespace).
1129
+ // Iterate non-empty blocks of the grid. Local SparseVoxelGrid stores one
1130
+ // byte per block type instead of upstream's packed 2-bit type words.
1131
+ // Each block is processed once; the inner loop also handles boundary cells
1132
+ // that straddle into neighboring blocks (so we don't need a separate pass
1133
+ // for orphan boundary cells along the negative grid edges).
1134
+ for (let w = 0; w < types.length; w++) {
1135
+ const word = types[w];
1136
+ if (word === 0) {
1137
+ continue;
1138
+ }
1139
+ let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
1140
+ const baseBlockIdx = w * BLOCKS_PER_WORD;
1141
+ while (nonEmpty) {
1142
+ const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
1143
+ const lane = bp >>> 1;
1144
+ const blockIdx = baseBlockIdx + lane;
1145
+ nonEmpty &= nonEmpty - 1;
1146
+ if (blockIdx >= totalBlocks) {
1147
+ break;
1148
+ }
1149
+ // Decode block coordinates.
1150
+ bx = blockIdx % nbx;
1151
+ const byBz = (blockIdx / nbx) | 0;
1152
+ by = byBz % nby;
1153
+ bz = (byBz / nby) | 0;
1154
+ // Populate the 3x3x3 neighbor table for this block. After this loop,
1155
+ // every per-cell occupancy query is a direct typed-array index.
1156
+ //
1157
+ // Track `allNeighborsSolid` so we can skip the entire cell loop
1158
+ // for blocks deep inside an obstruction, where every cubeIndex is
1159
+ // 255 and no triangles are emitted. On large carved scenes this
1160
+ // is the bulk of SOLID blocks and dominates marching-cubes runtime.
1161
+ let currentBlockIsSolid = false;
1162
+ let allNeighborsSolid = true;
1163
+ for (let dz = -1; dz <= 1; dz++) {
1164
+ const nbZ = bz + dz;
1165
+ for (let dy = -1; dy <= 1; dy++) {
1166
+ const nbY = by + dy;
1167
+ for (let dx = -1; dx <= 1; dx++) {
1168
+ const nbX = bx + dx;
1169
+ const slot = (dx + 1) + (dy + 1) * 3 + (dz + 1) * 9;
1170
+ if (nbX < 0 || nbY < 0 || nbZ < 0 ||
1171
+ nbX >= nbx || nbY >= nby || nbZ >= nbz) {
1172
+ neighborEntry[slot] = NEIGHBOR_EMPTY;
1173
+ allNeighborsSolid = false;
1174
+ continue;
1175
+ }
1176
+ const nbIdx = nbX + nbY * nbx + nbZ * bStride;
1177
+ const bt = readBlockType(types, nbIdx);
1178
+ if (bt === BLOCK_EMPTY) {
1179
+ neighborEntry[slot] = NEIGHBOR_EMPTY;
1180
+ allNeighborsSolid = false;
1181
+ }
1182
+ else if (bt === BLOCK_SOLID) {
1183
+ neighborEntry[slot] = NEIGHBOR_SOLID;
1184
+ if (dx === 0 && dy === 0 && dz === 0) {
1185
+ currentBlockIsSolid = true;
1186
+ }
1187
+ }
1188
+ else {
1189
+ neighborEntry[slot] = NEIGHBOR_MIXED;
1190
+ allNeighborsSolid = false;
1191
+ const ms = masks.slot(nbIdx);
1192
+ neighborMasks[slot * 2] = masks.lo[ms];
1193
+ neighborMasks[slot * 2 + 1] = masks.hi[ms];
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ // Block is fully interior to a solid region — every cubeIndex is
1199
+ // 255, every cell would emit 0 triangles. Skip the cell loop.
1200
+ if (currentBlockIsSolid && allNeighborsSolid) {
1201
+ continue;
1202
+ }
1203
+ // Iterate cell origins from -1 through 3 on each axis. The -1 and
1204
+ // 3 layers straddle block edges and close surfaces where no
1205
+ // neighboring block exists.
1206
+ for (let lz = -1; lz < 4; lz++) {
1207
+ const lzInside = lz >= 0 && lz <= 2;
1208
+ for (let ly = -1; ly < 4; ly++) {
1209
+ const lyInside = ly >= 0 && ly <= 2;
1210
+ for (let lx = -1; lx < 4; lx++) {
1211
+ // For solid blocks, the 27 cells with all axes in 0..2
1212
+ // are fully inside the block. All 8 corners are 1 so
1213
+ // cubeIndex == 255 and no triangles are emitted -- skip.
1214
+ if (currentBlockIsSolid && lzInside && lyInside && lx >= 0 && lx <= 2) {
1215
+ continue;
1216
+ }
1217
+ const vx = bx * 4 + lx;
1218
+ const vy = by * 4 + ly;
1219
+ const vz = bz * 4 + lz;
1220
+ // Determine which block owns this cell
1221
+ const ownerBx = vx >> 2;
1222
+ const ownerBy = vy >> 2;
1223
+ const ownerBz = vz >> 2;
1224
+ if (ownerBx !== bx || ownerBy !== by || ownerBz !== bz) {
1225
+ // Cell belongs to a different block — skip if that
1226
+ // block is non-empty (it will process the cell itself).
1227
+ if (ownerBx >= 0 && ownerBy >= 0 && ownerBz >= 0 &&
1228
+ ownerBx < nbx && ownerBy < nby && ownerBz < nbz) {
1229
+ const ownerIdx = ownerBx + ownerBy * nbx + ownerBz * bStride;
1230
+ if (readBlockType(types, ownerIdx) !== BLOCK_EMPTY) {
1231
+ continue;
1232
+ }
1233
+ }
1234
+ // Owner block doesn't exist or is out-of-bounds —
1235
+ // deduplicate so only the first neighboring block to
1236
+ // reach this cell emits triangles.
1237
+ const cellKey = (vx + 1) + (vy + 1) * strideX + (vz + 1) * strideXY;
1238
+ let oi = (Math.imul(cellKey | 0, 0x9E3779B9) >>> 0) & oMask;
1239
+ let oFound = false;
1240
+ while (true) {
1241
+ const ok = oKeys[oi];
1242
+ if (ok === cellKey) {
1243
+ oFound = true;
1244
+ break;
1245
+ }
1246
+ if (ok === -1) {
1247
+ break;
1248
+ }
1249
+ oi = (oi + 1) & oMask;
1250
+ }
1251
+ if (oFound) {
1252
+ continue;
1253
+ }
1254
+ oKeys[oi] = cellKey;
1255
+ oSize++;
1256
+ if (oSize > ((oCap * 0.7) | 0)) {
1257
+ oGrow();
1258
+ }
1259
+ }
1260
+ // Get corner values for this cell (8 corners)
1261
+ // Corners: (vx,vy,vz), (vx+1,vy,vz), (vx+1,vy+1,vz), (vx,vy+1,vz),
1262
+ // (vx,vy,vz+1), (vx+1,vy,vz+1), (vx+1,vy+1,vz+1), (vx,vy+1,vz+1)
1263
+ const c0 = isOccupiedLocal(vx, vy, vz) ? 1 : 0;
1264
+ const c1 = isOccupiedLocal(vx + 1, vy, vz) ? 1 : 0;
1265
+ const c2 = isOccupiedLocal(vx + 1, vy + 1, vz) ? 1 : 0;
1266
+ const c3 = isOccupiedLocal(vx, vy + 1, vz) ? 1 : 0;
1267
+ const c4 = isOccupiedLocal(vx, vy, vz + 1) ? 1 : 0;
1268
+ const c5 = isOccupiedLocal(vx + 1, vy, vz + 1) ? 1 : 0;
1269
+ const c6 = isOccupiedLocal(vx + 1, vy + 1, vz + 1) ? 1 : 0;
1270
+ const c7 = isOccupiedLocal(vx, vy + 1, vz + 1) ? 1 : 0;
1271
+ const cubeIndex = c0 | (c1 << 1) | (c2 << 2) | (c3 << 3) |
1272
+ (c4 << 4) | (c5 << 5) | (c6 << 6) | (c7 << 7);
1273
+ if (cubeIndex === 0 || cubeIndex === 255) {
1274
+ continue;
1275
+ }
1276
+ if (collectFlatFace(cubeIndex, vx, vy, vz)) {
1277
+ continue;
1278
+ }
1279
+ const edges = EDGE_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
1280
+ if (edges === 0) {
1281
+ continue;
1282
+ }
1283
+ const triRow = TRI_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
1284
+ const triLen = triRow.length;
1285
+ const usedMask = collectDiagFaces(triRow, vx, vy, vz);
1286
+ let neededEdges = 0;
1287
+ let emitTriLen = 0;
1288
+ for (let t = 0; t < triLen; t += 3) {
1289
+ const triIdx = (t / 3) | 0;
1290
+ if ((usedMask & (1 << triIdx)) !== 0) {
1291
+ continue;
1292
+ }
1293
+ neededEdges |= (1 << triRow[t]) | (1 << triRow[t + 1]) | (1 << triRow[t + 2]);
1294
+ emitTriLen += 3;
1295
+ }
1296
+ if (neededEdges === 0) {
1297
+ continue;
1298
+ }
1299
+ // Compute vertices on edges used by triangles that
1300
+ // were not absorbed into a merged binary-MC rectangle.
1301
+ if (neededEdges & 1) {
1302
+ edgeVerts[0] = getVertex(vx, vy, vz, 0);
1303
+ } // edge 0: x-axis at (vx, vy, vz)
1304
+ if (neededEdges & 2) {
1305
+ edgeVerts[1] = getVertex(vx + 1, vy, vz, 1);
1306
+ } // edge 1: y-axis at (vx+1, vy, vz)
1307
+ if (neededEdges & 4) {
1308
+ edgeVerts[2] = getVertex(vx, vy + 1, vz, 0);
1309
+ } // edge 2: x-axis at (vx, vy+1, vz)
1310
+ if (neededEdges & 8) {
1311
+ edgeVerts[3] = getVertex(vx, vy, vz, 1);
1312
+ } // edge 3: y-axis at (vx, vy, vz)
1313
+ if (neededEdges & 16) {
1314
+ edgeVerts[4] = getVertex(vx, vy, vz + 1, 0);
1315
+ } // edge 4: x-axis at (vx, vy, vz+1)
1316
+ if (neededEdges & 32) {
1317
+ edgeVerts[5] = getVertex(vx + 1, vy, vz + 1, 1);
1318
+ } // edge 5: y-axis at (vx+1, vy, vz+1)
1319
+ if (neededEdges & 64) {
1320
+ edgeVerts[6] = getVertex(vx, vy + 1, vz + 1, 0);
1321
+ } // edge 6: x-axis at (vx, vy+1, vz+1)
1322
+ if (neededEdges & 128) {
1323
+ edgeVerts[7] = getVertex(vx, vy, vz + 1, 1);
1324
+ } // edge 7: y-axis at (vx, vy, vz+1)
1325
+ if (neededEdges & 256) {
1326
+ edgeVerts[8] = getVertex(vx, vy, vz, 2);
1327
+ } // edge 8: z-axis at (vx, vy, vz)
1328
+ if (neededEdges & 512) {
1329
+ edgeVerts[9] = getVertex(vx + 1, vy, vz, 2);
1330
+ } // edge 9: z-axis at (vx+1, vy, vz)
1331
+ if (neededEdges & 1024) {
1332
+ edgeVerts[10] = getVertex(vx + 1, vy + 1, vz, 2);
1333
+ } // edge 10: z-axis at (vx+1, vy+1, vz)
1334
+ if (neededEdges & 2048) {
1335
+ edgeVerts[11] = getVertex(vx, vy + 1, vz, 2);
1336
+ } // edge 11: z-axis at (vx, vy+1, vz)
1337
+ // Emit triangles (reversed winding to face outward)
1338
+ ensureIndexCapacity(emitTriLen);
1339
+ for (let t = 0; t < triLen; t += 3) {
1340
+ const triIdx = (t / 3) | 0;
1341
+ if ((usedMask & (1 << triIdx)) !== 0) {
1342
+ continue;
1343
+ }
1344
+ indices[idxLen++] = edgeVerts[triRow[t]];
1345
+ indices[idxLen++] = edgeVerts[triRow[t + 2]];
1346
+ indices[idxLen++] = edgeVerts[triRow[t + 1]];
1347
+ }
1348
+ }
1349
+ }
1350
+ }
1351
+ }
1352
+ }
1353
+ flushFaceCells();
1354
+ return {
1355
+ positions: positions.slice(0, posLen),
1356
+ indices: indices.slice(0, idxLen)
1357
+ };
1358
+ }
1359
+ // ============================================================================
1360
+ // Marching Cubes Lookup Tables
1361
+ // ============================================================================
1362
+ // Standard tables from Paul Bourke's polygonising a scalar field.
1363
+ // EDGE_TABLE: 256 entries, each a 12-bit mask of which edges are intersected.
1364
+ // TRI_TABLE: 256 entries, each an array of edge indices forming triangles.
1365
+ const EDGE_TABLE = [
1366
+ 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
1367
+ 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
1368
+ 0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
1369
+ 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
1370
+ 0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
1371
+ 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
1372
+ 0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
1373
+ 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
1374
+ 0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c,
1375
+ 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
1376
+ 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc,
1377
+ 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
1378
+ 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c,
1379
+ 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
1380
+ 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc,
1381
+ 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
1382
+ 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
1383
+ 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
1384
+ 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
1385
+ 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
1386
+ 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
1387
+ 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
1388
+ 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
1389
+ 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460,
1390
+ 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
1391
+ 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0,
1392
+ 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
1393
+ 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230,
1394
+ 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
1395
+ 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190,
1396
+ 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
1397
+ 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
1398
+ ];
1399
+ const TRI_TABLE = [
1400
+ [],
1401
+ [0, 8, 3],
1402
+ [0, 1, 9],
1403
+ [1, 8, 3, 9, 8, 1],
1404
+ [1, 2, 10],
1405
+ [0, 8, 3, 1, 2, 10],
1406
+ [9, 2, 10, 0, 2, 9],
1407
+ [2, 8, 3, 2, 10, 8, 10, 9, 8],
1408
+ [3, 11, 2],
1409
+ [0, 11, 2, 8, 11, 0],
1410
+ [1, 9, 0, 2, 3, 11],
1411
+ [1, 11, 2, 1, 9, 11, 9, 8, 11],
1412
+ [3, 10, 1, 11, 10, 3],
1413
+ [0, 10, 1, 0, 8, 10, 8, 11, 10],
1414
+ [3, 9, 0, 3, 11, 9, 11, 10, 9],
1415
+ [9, 8, 10, 10, 8, 11],
1416
+ [4, 7, 8],
1417
+ [4, 3, 0, 7, 3, 4],
1418
+ [0, 1, 9, 8, 4, 7],
1419
+ [4, 1, 9, 4, 7, 1, 7, 3, 1],
1420
+ [1, 2, 10, 8, 4, 7],
1421
+ [3, 4, 7, 3, 0, 4, 1, 2, 10],
1422
+ [9, 2, 10, 9, 0, 2, 8, 4, 7],
1423
+ [2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4],
1424
+ [8, 4, 7, 3, 11, 2],
1425
+ [11, 4, 7, 11, 2, 4, 2, 0, 4],
1426
+ [9, 0, 1, 8, 4, 7, 2, 3, 11],
1427
+ [4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1],
1428
+ [3, 10, 1, 3, 11, 10, 7, 8, 4],
1429
+ [1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4],
1430
+ [4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3],
1431
+ [4, 7, 11, 4, 11, 9, 9, 11, 10],
1432
+ [9, 5, 4],
1433
+ [9, 5, 4, 0, 8, 3],
1434
+ [0, 5, 4, 1, 5, 0],
1435
+ [8, 5, 4, 8, 3, 5, 3, 1, 5],
1436
+ [1, 2, 10, 9, 5, 4],
1437
+ [3, 0, 8, 1, 2, 10, 4, 9, 5],
1438
+ [5, 2, 10, 5, 4, 2, 4, 0, 2],
1439
+ [2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8],
1440
+ [9, 5, 4, 2, 3, 11],
1441
+ [0, 11, 2, 0, 8, 11, 4, 9, 5],
1442
+ [0, 5, 4, 0, 1, 5, 2, 3, 11],
1443
+ [2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5],
1444
+ [10, 3, 11, 10, 1, 3, 9, 5, 4],
1445
+ [4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10],
1446
+ [5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3],
1447
+ [5, 4, 8, 5, 8, 10, 10, 8, 11],
1448
+ [9, 7, 8, 5, 7, 9],
1449
+ [9, 3, 0, 9, 5, 3, 5, 7, 3],
1450
+ [0, 7, 8, 0, 1, 7, 1, 5, 7],
1451
+ [1, 5, 3, 3, 5, 7],
1452
+ [9, 7, 8, 9, 5, 7, 10, 1, 2],
1453
+ [10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3],
1454
+ [8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2],
1455
+ [2, 10, 5, 2, 5, 3, 3, 5, 7],
1456
+ [7, 9, 5, 7, 8, 9, 3, 11, 2],
1457
+ [9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11],
1458
+ [2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7],
1459
+ [11, 2, 1, 11, 1, 7, 7, 1, 5],
1460
+ [9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11],
1461
+ [5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0],
1462
+ [11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0],
1463
+ [11, 10, 5, 7, 11, 5],
1464
+ [10, 6, 5],
1465
+ [0, 8, 3, 5, 10, 6],
1466
+ [9, 0, 1, 5, 10, 6],
1467
+ [1, 8, 3, 1, 9, 8, 5, 10, 6],
1468
+ [1, 6, 5, 2, 6, 1],
1469
+ [1, 6, 5, 1, 2, 6, 3, 0, 8],
1470
+ [9, 6, 5, 9, 0, 6, 0, 2, 6],
1471
+ [5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8],
1472
+ [2, 3, 11, 10, 6, 5],
1473
+ [11, 0, 8, 11, 2, 0, 10, 6, 5],
1474
+ [0, 1, 9, 2, 3, 11, 5, 10, 6],
1475
+ [5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11],
1476
+ [6, 3, 11, 6, 5, 3, 5, 1, 3],
1477
+ [0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6],
1478
+ [3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9],
1479
+ [6, 5, 9, 6, 9, 11, 11, 9, 8],
1480
+ [5, 10, 6, 4, 7, 8],
1481
+ [4, 3, 0, 4, 7, 3, 6, 5, 10],
1482
+ [1, 9, 0, 5, 10, 6, 8, 4, 7],
1483
+ [10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4],
1484
+ [6, 1, 2, 6, 5, 1, 4, 7, 8],
1485
+ [1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7],
1486
+ [8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6],
1487
+ [7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9],
1488
+ [3, 11, 2, 7, 8, 4, 10, 6, 5],
1489
+ [5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11],
1490
+ [0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6],
1491
+ [9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6],
1492
+ [8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6],
1493
+ [5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11],
1494
+ [0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7],
1495
+ [6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9],
1496
+ [10, 4, 9, 6, 4, 10],
1497
+ [4, 10, 6, 4, 9, 10, 0, 8, 3],
1498
+ [10, 0, 1, 10, 6, 0, 6, 4, 0],
1499
+ [8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10],
1500
+ [1, 4, 9, 1, 2, 4, 2, 6, 4],
1501
+ [3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4],
1502
+ [0, 2, 4, 4, 2, 6],
1503
+ [8, 3, 2, 8, 2, 4, 4, 2, 6],
1504
+ [10, 4, 9, 10, 6, 4, 11, 2, 3],
1505
+ [0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6],
1506
+ [3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10],
1507
+ [6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1],
1508
+ [9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3],
1509
+ [8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1],
1510
+ [3, 11, 6, 3, 6, 0, 0, 6, 4],
1511
+ [6, 4, 8, 11, 6, 8],
1512
+ [7, 10, 6, 7, 8, 10, 8, 9, 10],
1513
+ [0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10],
1514
+ [10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0],
1515
+ [10, 6, 7, 10, 7, 1, 1, 7, 3],
1516
+ [1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7],
1517
+ [2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9],
1518
+ [7, 8, 0, 7, 0, 6, 6, 0, 2],
1519
+ [7, 3, 2, 6, 7, 2],
1520
+ [2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7],
1521
+ [2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7],
1522
+ [1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11],
1523
+ [11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1],
1524
+ [8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6],
1525
+ [0, 9, 1, 11, 6, 7],
1526
+ [7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0],
1527
+ [7, 11, 6],
1528
+ [7, 6, 11],
1529
+ [3, 0, 8, 11, 7, 6],
1530
+ [0, 1, 9, 11, 7, 6],
1531
+ [8, 1, 9, 8, 3, 1, 11, 7, 6],
1532
+ [10, 1, 2, 6, 11, 7],
1533
+ [1, 2, 10, 3, 0, 8, 6, 11, 7],
1534
+ [2, 9, 0, 2, 10, 9, 6, 11, 7],
1535
+ [6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8],
1536
+ [7, 2, 3, 6, 2, 7],
1537
+ [7, 0, 8, 7, 6, 0, 6, 2, 0],
1538
+ [2, 7, 6, 2, 3, 7, 0, 1, 9],
1539
+ [1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6],
1540
+ [10, 7, 6, 10, 1, 7, 1, 3, 7],
1541
+ [10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8],
1542
+ [0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7],
1543
+ [7, 6, 10, 7, 10, 8, 8, 10, 9],
1544
+ [6, 8, 4, 11, 8, 6],
1545
+ [3, 6, 11, 3, 0, 6, 0, 4, 6],
1546
+ [8, 6, 11, 8, 4, 6, 9, 0, 1],
1547
+ [9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6],
1548
+ [6, 8, 4, 6, 11, 8, 2, 10, 1],
1549
+ [1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6],
1550
+ [4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9],
1551
+ [10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3],
1552
+ [8, 2, 3, 8, 4, 2, 4, 6, 2],
1553
+ [0, 4, 2, 4, 6, 2],
1554
+ [1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8],
1555
+ [1, 9, 4, 1, 4, 2, 2, 4, 6],
1556
+ [8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1],
1557
+ [10, 1, 0, 10, 0, 6, 6, 0, 4],
1558
+ [4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3],
1559
+ [10, 9, 4, 6, 10, 4],
1560
+ [4, 9, 5, 7, 6, 11],
1561
+ [0, 8, 3, 4, 9, 5, 11, 7, 6],
1562
+ [5, 0, 1, 5, 4, 0, 7, 6, 11],
1563
+ [11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5],
1564
+ [9, 5, 4, 10, 1, 2, 7, 6, 11],
1565
+ [6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5],
1566
+ [7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2],
1567
+ [3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6],
1568
+ [7, 2, 3, 7, 6, 2, 5, 4, 9],
1569
+ [9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7],
1570
+ [3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0],
1571
+ [6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8],
1572
+ [9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7],
1573
+ [1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4],
1574
+ [4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10],
1575
+ [7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10],
1576
+ [6, 9, 5, 6, 11, 9, 11, 8, 9],
1577
+ [3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5],
1578
+ [0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11],
1579
+ [6, 11, 3, 6, 3, 5, 5, 3, 1],
1580
+ [1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6],
1581
+ [0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10],
1582
+ [11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5],
1583
+ [6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3],
1584
+ [5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2],
1585
+ [9, 5, 6, 9, 6, 0, 0, 6, 2],
1586
+ [1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8],
1587
+ [1, 5, 6, 2, 1, 6],
1588
+ [1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6],
1589
+ [10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0],
1590
+ [0, 3, 8, 5, 6, 10],
1591
+ [10, 5, 6],
1592
+ [11, 5, 10, 7, 5, 11],
1593
+ [11, 5, 10, 11, 7, 5, 8, 3, 0],
1594
+ [5, 11, 7, 5, 10, 11, 1, 9, 0],
1595
+ [10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1],
1596
+ [11, 1, 2, 11, 7, 1, 7, 5, 1],
1597
+ [0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11],
1598
+ [9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7],
1599
+ [7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2],
1600
+ [2, 5, 10, 2, 3, 5, 3, 7, 5],
1601
+ [8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5],
1602
+ [9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2],
1603
+ [9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2],
1604
+ [1, 3, 5, 3, 7, 5],
1605
+ [0, 8, 7, 0, 7, 1, 1, 7, 5],
1606
+ [9, 0, 3, 9, 3, 5, 5, 3, 7],
1607
+ [9, 8, 7, 5, 9, 7],
1608
+ [5, 8, 4, 5, 10, 8, 10, 11, 8],
1609
+ [5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0],
1610
+ [0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5],
1611
+ [10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4],
1612
+ [2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8],
1613
+ [0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11],
1614
+ [0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5],
1615
+ [9, 4, 5, 2, 11, 3],
1616
+ [2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4],
1617
+ [5, 10, 2, 5, 2, 4, 4, 2, 0],
1618
+ [3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9],
1619
+ [5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2],
1620
+ [8, 4, 5, 8, 5, 3, 3, 5, 1],
1621
+ [0, 4, 5, 1, 0, 5],
1622
+ [8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5],
1623
+ [9, 4, 5],
1624
+ [4, 11, 7, 4, 9, 11, 9, 10, 11],
1625
+ [0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11],
1626
+ [1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11],
1627
+ [3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4],
1628
+ [4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2],
1629
+ [9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3],
1630
+ [11, 7, 4, 11, 4, 2, 2, 4, 0],
1631
+ [11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4],
1632
+ [2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9],
1633
+ [9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7],
1634
+ [3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10],
1635
+ [1, 10, 2, 8, 7, 4],
1636
+ [4, 9, 1, 4, 1, 7, 7, 1, 3],
1637
+ [4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1],
1638
+ [4, 0, 3, 7, 4, 3],
1639
+ [4, 8, 7],
1640
+ [9, 10, 8, 10, 11, 8],
1641
+ [3, 0, 9, 3, 9, 11, 11, 9, 10],
1642
+ [0, 1, 10, 0, 10, 8, 8, 10, 11],
1643
+ [3, 1, 10, 11, 3, 10],
1644
+ [1, 2, 11, 1, 11, 9, 9, 11, 8],
1645
+ [3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9],
1646
+ [0, 2, 11, 8, 0, 11],
1647
+ [3, 2, 11],
1648
+ [2, 3, 8, 2, 8, 10, 10, 8, 9],
1649
+ [9, 10, 2, 0, 9, 2],
1650
+ [2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8],
1651
+ [1, 10, 2],
1652
+ [1, 3, 8, 9, 1, 8],
1653
+ [0, 9, 1],
1654
+ [0, 3, 8],
1655
+ []
1656
+ ];
1657
+ export { marchingCubes };