@woosh/meep-engine 2.43.1 → 2.43.4

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 (138) hide show
  1. package/core/binary/BinaryBuffer.js +13 -1
  2. package/core/binary/BitSet.js +2 -2
  3. package/core/bvh2/aabb3/aabb3_array_combine.js +2 -2
  4. package/core/collection/RingBuffer.js +4 -2
  5. package/core/collection/RingBuffer.spec.js +59 -0
  6. package/core/collection/array/ArrayIteratorRandom.js +1 -1
  7. package/core/collection/{ArrayUtils.spec.js → array/arrayPickBestElement.spec.js} +1 -1
  8. package/core/collection/array/arrayPickBestElements.js +51 -0
  9. package/core/collection/array/arrayPickMinElement.js +43 -0
  10. package/core/collection/array/arrayQuickSort.js +1 -1
  11. package/core/collection/array/arraySetSortingDiff.js +1 -1
  12. package/core/collection/array/arraySwapElements.js +12 -0
  13. package/core/collection/array/array_range_equal_strict.js +22 -0
  14. package/core/collection/array/groupArrayBy.js +42 -0
  15. package/core/collection/array/isArrayEqual.js +50 -0
  16. package/core/collection/array/randomMultipleFromArray.js +34 -0
  17. package/core/collection/array/randomizeArrayElementOrder.js +23 -0
  18. package/core/color/sRGB_to_linear.js +9 -4
  19. package/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +1 -1
  20. package/core/geom/3d/aabb/aabb3_build_frustum.js +1 -1
  21. package/core/geom/3d/aabb/compute_aabb_from_points.js +1 -1
  22. package/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +3 -1
  23. package/core/geom/3d/morton/v3_morton_encode_transformed.spec.js +20 -0
  24. package/core/geom/3d/plane/orient3d_fast.js +11 -10
  25. package/core/geom/3d/plane/orient3d_robust.js +41 -0
  26. package/core/geom/3d/plane/plane_computeConvex3PlaneIntersection.js +0 -23
  27. package/core/geom/3d/plane/plane_three_compute_convex3_plane_intersection.js +24 -0
  28. package/core/geom/3d/shape/UnionShape3D.js +1 -1
  29. package/core/geom/3d/sphere/harmonics/README.md +15 -0
  30. package/core/geom/3d/sphere/harmonics/sh3_add.js +21 -0
  31. package/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +618 -0
  32. package/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +49 -0
  33. package/core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js +53 -0
  34. package/core/geom/3d/tetrahedra/README.md +10 -1
  35. package/core/geom/3d/tetrahedra/TetrahedralMesh.js +650 -0
  36. package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +233 -0
  37. package/core/geom/3d/tetrahedra/build_tetrahedral_mesh_buffer_geometry.js +75 -0
  38. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +2 -2
  39. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.spec.js +4 -4
  40. package/core/geom/3d/tetrahedra/delaunay/Cavity.js +49 -7
  41. package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +51 -17
  42. package/core/geom/3d/tetrahedra/delaunay/debug_validate_mesh.js +19 -0
  43. package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +191 -0
  44. package/core/geom/3d/tetrahedra/delaunay/push_boundary_with_validation.js +27 -0
  45. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +59 -43
  46. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.js +77 -0
  47. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.spec.js +30 -0
  48. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_towards_containing_tetrahedron.js +58 -0
  49. package/core/geom/3d/tetrahedra/delaunay/validate_cavity_boundary.js +60 -0
  50. package/core/geom/3d/tetrahedra/{point_in_tetrahedron_circumsphere.js → in_sphere_fast.js} +11 -13
  51. package/core/geom/3d/tetrahedra/in_sphere_robust.js +53 -0
  52. package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +44 -35
  53. package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.js +83 -0
  54. package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.spec.js +24 -0
  55. package/core/geom/3d/tetrahedra/tetrahedron_contains_point.spec.js +66 -0
  56. package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +166 -0
  57. package/core/geom/3d/util/make_justified_point_grid.js +31 -0
  58. package/core/geom/Bezier.js +0 -27
  59. package/core/geom/Plane.js +0 -4
  60. package/core/geom/packing/miniball/Subspan.js +2 -2
  61. package/core/geom/v3_lerp.js +6 -1
  62. package/core/math/isqrt.js +28 -0
  63. package/core/math/isqrt.spec.js +9 -0
  64. package/core/math/max.spec.js +25 -0
  65. package/core/math/min2.spec.js +25 -0
  66. package/core/model/node-graph/node/NodeInstance.js +3 -3
  67. package/core/primitives/strings/prefixTree/PrefixTreeLeaf.js +1 -1
  68. package/core/process/delay.js +5 -0
  69. package/core/process/task/util/randomCountTask.js +1 -1
  70. package/editor/Editor.js +3 -0
  71. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +195 -11
  72. package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.js +16 -0
  73. package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +4 -0
  74. package/editor/ecs/component/editors/primitive/ArrayEditor.js +1 -1
  75. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +6 -0
  76. package/editor/view/ecs/components/common/AutoCanvasView.js +13 -25
  77. package/engine/EngineHarness.js +11 -5
  78. package/engine/asset/AssetManager.d.ts +5 -1
  79. package/engine/asset/AssetManager.js +50 -15
  80. package/engine/asset/AssetManager.spec.js +17 -11
  81. package/engine/asset/AssetRequest.js +57 -0
  82. package/engine/asset/loaders/ArrayBufferLoader.js +22 -0
  83. package/engine/asset/loaders/AssetLoader.js +1 -1
  84. package/engine/ecs/System.js +1 -1
  85. package/engine/ecs/dynamic_actions/DynamicActorSystem.js +1 -1
  86. package/engine/ecs/terrain/ecs/TerrainSystem.js +7 -1
  87. package/engine/ecs/transform/copy_three_transform.js +15 -0
  88. package/engine/graphics/FrameRunner.js +5 -9
  89. package/engine/graphics/ecs/animation/animator/AnimationClipDefinition.js +1 -1
  90. package/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +1 -1
  91. package/engine/graphics/ecs/camera/Camera.js +1 -10
  92. package/engine/graphics/ecs/camera/CameraSystem.js +8 -8
  93. package/engine/graphics/ecs/camera/ProjectionType.js +9 -0
  94. package/engine/graphics/ecs/camera/build_three_camera_object.js +3 -3
  95. package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +59 -4
  96. package/engine/graphics/ecs/light/Light.js +6 -1
  97. package/engine/graphics/ecs/light/LightSystem.d.ts +1 -1
  98. package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +2 -17
  99. package/engine/graphics/geometry/VertexDataSpec.js +1 -1
  100. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +2 -2
  101. package/engine/graphics/impostors/octahedral/prototypeBaker.js +3 -3
  102. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
  103. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +3 -1
  104. package/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.js +1 -1
  105. package/engine/graphics/render/forward_plus/LightManager.js +1 -1
  106. package/engine/graphics/render/forward_plus/LightManager.spec.js +4 -0
  107. package/engine/graphics/render/forward_plus/computeFrustumCorners.js +4 -2
  108. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
  109. package/engine/graphics/render/layers/RenderLayerUtils.js +2 -2
  110. package/engine/graphics/sh3/LightProbeVolume.js +595 -0
  111. package/engine/graphics/sh3/SH3VisualisationMaterial.js +79 -0
  112. package/engine/graphics/sh3/prototypeSH3Probe.js +427 -0
  113. package/engine/graphics/sh3/visualise_probe.js +40 -0
  114. package/engine/graphics/shaders/DenoiseShader.js +1 -1
  115. package/engine/graphics/texture/atlas/AtlasPatch.js +11 -3
  116. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +2 -2
  117. package/engine/graphics/texture/atlas/TextureAtlas.js +22 -4
  118. package/engine/graphics/texture/atlas/TextureAtlas.spec.js +22 -0
  119. package/engine/graphics/texture/sampler/Sampler2D.js +0 -64
  120. package/engine/graphics/texture/sampler/Sampler2D.spec.js +2 -1
  121. package/engine/graphics/texture/sampler/sampler2d_combine.js +67 -0
  122. package/engine/intelligence/behavior/ecs/BehaviorSystem.spec.js +0 -3
  123. package/engine/intelligence/blackboard/AbstractBlackboard.d.ts +1 -1
  124. package/engine/network/PriorityFetch.js +192 -0
  125. package/engine/simulation/DormandPrince.js +1 -1
  126. package/engine/ui/DraggableAspect.js +0 -1
  127. package/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
  128. package/package.json +2 -1
  129. package/samples/terrain/from_image_2.js +127 -82
  130. package/view/elements/CanvasView.js +7 -1
  131. package/view/elements/image/HTMLElementCacheKey.js +1 -1
  132. package/view/util/DomSizeObserver.js +3 -5
  133. package/core/collection/ArrayUtils.js +0 -263
  134. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +0 -48
  135. package/core/geom/3d/tetrahedra/hxt/a.js +0 -524
  136. package/core/geom/3d/tetrahedra/hxt/hxt.js +0 -140
  137. package/core/geom/3d/tetrahedra/hxt/hxt.wasm +0 -0
  138. package/core/geom/3d/tetrahedra/tetrahedra_collection.js +0 -383
@@ -0,0 +1,650 @@
1
+ import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
2
+ import { typed_array_copy } from "../../../collection/array/typed/typed_array_copy.js";
3
+ import { max3 } from "../../../math/max3.js";
4
+ import { assert } from "../../../assert.js";
5
+ import { array_copy } from "../../../collection/array/copyArray.js";
6
+ import { array_quick_sort_by_comparator } from "../../../collection/array/arrayQuickSort.js";
7
+ import { compareNumbersDescending } from "../../../function/Functions.js";
8
+ import { BinaryBuffer } from "../../../binary/BinaryBuffer.js";
9
+ import { Base64 } from "../../../binary/Base64.js";
10
+
11
+ /**
12
+ * @readonly
13
+ * @type {number}
14
+ */
15
+ const LAYOUT_TETRA_WORD_COUNT = 8;
16
+
17
+ /**
18
+ * Size in bytes of a single tetrahedron record
19
+ * @readonly
20
+ * @type {number}
21
+ */
22
+ export const LAYOUT_TETRA_BYTE_SIZE = LAYOUT_TETRA_WORD_COUNT * 4;
23
+
24
+ /**
25
+ * @readonly
26
+ * @type {number}
27
+ */
28
+ export const INVALID_NEIGHBOUR = 0xFFFFFFFF;
29
+
30
+ /**
31
+ * @readonly
32
+ * @type {number}
33
+ */
34
+ export const MAX_TET_INDEX = 0xFFFFFFFC;
35
+
36
+ /**
37
+ * @readonly
38
+ * @type {number}
39
+ */
40
+ const DEFAULT_INITIAL_SIZE = 128;
41
+
42
+ /**
43
+ * @readonly
44
+ * @type {number}
45
+ */
46
+ const CAPACITY_GROW_MULTIPLIER = 1.2;
47
+
48
+ /**
49
+ * @readonly
50
+ * @type {number}
51
+ */
52
+ const CAPACITY_GROW_MIN_STEP = 32;
53
+
54
+ /**
55
+ * Only keeps track of tetrahedra, actual point coordinates are stored outside.
56
+ * For most useful operations point coordinates are passed in as an extra argument.
57
+ *
58
+ * Binary Layout:
59
+ * vertex_id_a :: uint32
60
+ * vertex_id_b :: uint32
61
+ * vertex_id_c :: uint32
62
+ * vertex_id_d :: uint32
63
+ * neighbour_a :: uint32 - neighbour tetrahedron, opposite to vertex A
64
+ * neighbour_b :: uint32 - neighbour tetrahedron, opposite to vertex B
65
+ * neighbour_c :: uint32 - neighbour tetrahedron, opposite to vertex C
66
+ * neighbour_d :: uint32 - neighbour tetrahedron, opposite to vertex D
67
+ * Layout is similar to [1], but is interleaved for better cache locality.
68
+ * Also note that sub-determinants are not included, these are only needed for building the mesh, we excluded them to keep structure clean and more compact.
69
+ *
70
+ * Neighbours are encoded in the following manner:
71
+ * MSB -> [tet_id:30bit][opposite_corner_index:2bit] <- LSB
72
+ * Code to get tet index: encoded >> 2
73
+ * Code to get corner index: encoded & 3
74
+ *
75
+ * @see [1] 2018 "One machine, one minute, three billion tetrahedra" by Célestin Marot, Jeanne Pellerin and Jean-François Remacle
76
+ * @see https://git.immc.ucl.ac.be/hextreme/hxt_seqdel (C source code for [1])
77
+ */
78
+ export class TetrahedralMesh {
79
+ /**
80
+ *
81
+ * @param {number} [initial_size]
82
+ */
83
+ constructor(initial_size = DEFAULT_INITIAL_SIZE) {
84
+ assert.isNonNegativeInteger(initial_size, 'initial_size');
85
+
86
+ /**
87
+ *
88
+ * @type {ArrayBuffer}
89
+ * @private
90
+ */
91
+ this.__buffer = new ArrayBuffer(initial_size * LAYOUT_TETRA_BYTE_SIZE);
92
+
93
+ /**
94
+ *
95
+ * @type {Uint32Array}
96
+ * @private
97
+ */
98
+ this.__data_uint32 = new Uint32Array(this.__buffer);
99
+
100
+ /**
101
+ *
102
+ * @type {DataView}
103
+ * @private
104
+ */
105
+ this.__view = new DataView(this.__buffer);
106
+
107
+ /**
108
+ *
109
+ * @type {number}
110
+ * @private
111
+ */
112
+ this.__capacity = initial_size;
113
+
114
+ /**
115
+ *
116
+ * @type {number}
117
+ * @private
118
+ */
119
+ this.__used_end = 0;
120
+
121
+ /**
122
+ * Unused slots
123
+ * @type {number[]}
124
+ * @private
125
+ */
126
+ this.__free = [];
127
+
128
+ /**
129
+ *
130
+ * @type {number}
131
+ * @private
132
+ */
133
+ this.__free_pointer = 0;
134
+ }
135
+
136
+ /**
137
+ * Traverse tetrahedrons
138
+ * @param {(tet:number,mesh:TetrahedralMesh)=>*} visitor
139
+ * @param {*} [thisArg]
140
+ */
141
+ forEach(visitor, thisArg) {
142
+ for (let i = 0; i < this.__used_end; i++) {
143
+ if (!this.exists(i)) {
144
+ continue;
145
+ }
146
+
147
+ visitor.call(thisArg, i, this);
148
+ }
149
+ }
150
+
151
+ /**
152
+ *
153
+ * @param {number} capacity
154
+ */
155
+ setCapacity(capacity) {
156
+ assert.isNonNegativeInteger(capacity, 'capacity');
157
+
158
+ if (capacity === this.__capacity) {
159
+ // do nothing
160
+ return;
161
+ }
162
+
163
+ if (capacity < this.__capacity && capacity < this.__used_end) {
164
+ throw new Error('Reducing capacity would result in dropping information. This is an illegal operation. If you need to reduce capacity - either drop data or compact the layout first.');
165
+ }
166
+
167
+ // allocate new buffer
168
+ const new_buffer = new ArrayBuffer(capacity * LAYOUT_TETRA_BYTE_SIZE);
169
+
170
+ // move data across from old buffer to the new one
171
+ const destination_uint8 = new Uint8Array(new_buffer);
172
+ const source_uint8 = new Uint8Array(this.__buffer);
173
+
174
+ typed_array_copy(source_uint8, destination_uint8);
175
+
176
+ // set new buffer
177
+ this.__buffer = new_buffer;
178
+ this.__view = new DataView(new_buffer);
179
+ this.__data_uint32 = new Uint32Array(new_buffer);
180
+
181
+ // write new capacity
182
+ this.__capacity = capacity;
183
+ }
184
+
185
+ /**
186
+ *
187
+ * @return {number}
188
+ */
189
+ getCapacity() {
190
+ return this.__capacity;
191
+ }
192
+
193
+ /**
194
+ * How many tetrahedrons are contained in the mesh
195
+ * @return {number}
196
+ */
197
+ size() {
198
+ return this.__used_end;
199
+ }
200
+
201
+ /**
202
+ * Grow capacity to at least the specified size
203
+ * @param {number} capacity
204
+ */
205
+ growCapacity(capacity) {
206
+
207
+ const existing_capacity = this.__capacity;
208
+
209
+ const new_capacity = max3(
210
+ capacity,
211
+ Math.ceil(existing_capacity * CAPACITY_GROW_MULTIPLIER),
212
+ existing_capacity + CAPACITY_GROW_MIN_STEP
213
+ );
214
+
215
+ this.setCapacity(new_capacity);
216
+ }
217
+
218
+ /**
219
+ * NOTE: this method can be quite slow in cases of sparse allocation, please prefer not to use it
220
+ * @param {number} tet
221
+ * @return {boolean}
222
+ */
223
+ exists(tet) {
224
+
225
+ if (tet < 0 || tet >= this.__used_end) {
226
+ return false;
227
+ }
228
+
229
+ for (let i = 0; i < this.__free_pointer; i++) {
230
+ const free = this.__free[i];
231
+
232
+ if (tet === free) {
233
+ return false;
234
+ }
235
+ }
236
+
237
+ return true;
238
+ }
239
+
240
+ /**
241
+ * NOTE: the neighbour value must be encoded, see format specification for details
242
+ * @param {number} tetra_index
243
+ * @param {number} neighbour_index
244
+ * @returns {number} index of the neighbour encoded with the opposite corner
245
+ */
246
+ getNeighbour(tetra_index, neighbour_index) {
247
+ assert.isNonNegativeInteger(tetra_index, 'tetra_index');
248
+ assert.ok(this.exists(tetra_index), 'tetrahedron does not exist');
249
+
250
+ assert.isNonNegativeInteger(neighbour_index, 'neighbour_index');
251
+
252
+ assert.lessThan(neighbour_index, 4, 'neighbour_index');
253
+
254
+ const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
255
+ return this.__view.getUint32(tetra_address + (4 + neighbour_index) * 4);
256
+ }
257
+
258
+ /**
259
+ * NOTE: the neighbour value must be encoded, see format specification for details
260
+ * @param {number} tetra_index
261
+ * @param {number} neighbour_index which neighbour to set (0..11)
262
+ * @param {number} neighbour index of the neighbour encoded with the opposite corner
263
+ */
264
+ setNeighbour(tetra_index, neighbour_index, neighbour) {
265
+
266
+ assert.isNonNegativeInteger(tetra_index, 'tetra_index');
267
+ assert.ok(this.exists(tetra_index), 'tetrahedron does not exist');
268
+
269
+ assert.isNonNegativeInteger(neighbour_index, 'neighbour_index');
270
+ assert.isNonNegativeInteger(neighbour, 'neighbour');
271
+
272
+ assert.lessThan(neighbour_index, 4, 'neighbour_index');
273
+
274
+ const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
275
+ return this.__view.setUint32(tetra_address + (4 + neighbour_index) * 4, neighbour);
276
+ }
277
+
278
+
279
+ /**
280
+ *
281
+ * @param {number} tet_index
282
+ * @param {number} point_index should be an integer between 0 and 3
283
+ * @returns {number}
284
+ */
285
+ getVertexIndex(tet_index, point_index) {
286
+ assert.isNonNegativeInteger(tet_index, 'tet_index');
287
+ assert.lessThanOrEqual(tet_index, MAX_TET_INDEX, 'max index exceeded');
288
+ //assert.ok(this.exists(tet_index), 'tetrahedron does not exist');
289
+
290
+ assert.isNonNegativeInteger(point_index, 'point_index');
291
+ assert.lessThan(point_index, 4, 'point_index must be less than 4');
292
+
293
+
294
+ return this.__view.getUint32(tet_index * LAYOUT_TETRA_BYTE_SIZE + point_index * 4);
295
+ }
296
+
297
+ /**
298
+ *
299
+ * @param {number} tet_index
300
+ * @param {number} point_index
301
+ * @param {number} vertex
302
+ */
303
+ setVertexIndex(tet_index, point_index, vertex) {
304
+ assert.isNonNegativeInteger(tet_index, 'tet_index');
305
+ assert.lessThanOrEqual(tet_index, MAX_TET_INDEX, 'max index exceeded');
306
+ //assert.ok(this.exists(tet_index), 'tetrahedron does not exist');
307
+
308
+ assert.isNonNegativeInteger(point_index, 'point_index');
309
+ assert.lessThan(point_index, 4, 'point_index must be less than 4');
310
+
311
+ assert.isNonNegativeInteger(vertex, 'vertex');
312
+
313
+
314
+ return this.__view.setUint32(
315
+ tet_index * LAYOUT_TETRA_BYTE_SIZE + point_index * 4,
316
+ vertex
317
+ );
318
+ }
319
+
320
+ /**
321
+ * Whether a given tetrahedron contains vertex with a given index
322
+ * @param {number} tet
323
+ * @param {number} vertex
324
+ * @return {boolean}
325
+ */
326
+ tetContainsVertex(tet, vertex) {
327
+ for (let i = 0; i < 4; i++) {
328
+ if (this.getVertexIndex(tet, i) === vertex) {
329
+ return true;
330
+ }
331
+ }
332
+
333
+ return false;
334
+ }
335
+
336
+
337
+ /**
338
+ * Allocate empty tet
339
+ * NOTE: the tet memory might be dirty, please make sure you set/clear it as necessary
340
+ * @return {number} index of allocated tetrahedron
341
+ */
342
+ allocate() {
343
+ if (this.__free_pointer > 0) {
344
+ this.__free_pointer--;
345
+ return this.__free[this.__free_pointer];
346
+ }
347
+
348
+ const tetra_index = this.__used_end;
349
+
350
+ this.__used_end++;
351
+
352
+ if (tetra_index >= this.__capacity) {
353
+ // needs to be increased in size
354
+ this.growCapacity(tetra_index);
355
+ }
356
+
357
+ // initialize neighbours
358
+ for (let i = 0; i < 4; i++) {
359
+ this.setNeighbour(tetra_index, i, INVALID_NEIGHBOUR);
360
+ }
361
+
362
+ //assert(this.validateLength());
363
+
364
+
365
+ // assert(this.validateLength());
366
+
367
+ return tetra_index;
368
+ }
369
+
370
+ /**
371
+ *
372
+ * @param {number[]|Float32Array} points
373
+ * @param {number} a
374
+ * @param {number} b
375
+ * @param {number} c
376
+ * @param {number} d
377
+ * @returns {number} index of the new tetrahedron
378
+ */
379
+ append(points, a, b, c, d) {
380
+ const tetra_index = this.allocate();
381
+
382
+ const address = tetra_index * LAYOUT_TETRA_BYTE_SIZE;
383
+
384
+ const view = this.__view;
385
+
386
+ view.setUint32(address, a);
387
+ view.setUint32(address + 4, b);
388
+ view.setUint32(address + 8, c);
389
+ view.setUint32(address + 12, d);
390
+
391
+ // set neighbours
392
+ view.setUint32(address + 16, INVALID_NEIGHBOUR);
393
+ view.setUint32(address + 20, INVALID_NEIGHBOUR);
394
+ view.setUint32(address + 24, INVALID_NEIGHBOUR);
395
+ view.setUint32(address + 28, INVALID_NEIGHBOUR);
396
+
397
+ return tetra_index;
398
+ }
399
+
400
+ /**
401
+ * Sets back-links on neighbours to this tet to INVALID_NEIGHBOUR basically making them into mesh surface
402
+ * This is a useful method for when you want to completely remove a given tet from the mesh to make sure that no dangling references will remain
403
+ * @param {number} tetra_index
404
+ */
405
+ disconnect(tetra_index) {
406
+ // find neighbours and remove reference to self
407
+ for (let i = 0; i < 4; i++) {
408
+ const neighbour_encoded = this.getNeighbour(tetra_index, i);
409
+
410
+ if (neighbour_encoded === INVALID_NEIGHBOUR) {
411
+ // no neighbour
412
+ continue;
413
+ }
414
+
415
+ // get tetrahedra index and point index
416
+ const neighbour_index = neighbour_encoded >> 2;
417
+ const neighbour_point = neighbour_encoded & 3;
418
+
419
+ // clear reference to self
420
+ this.setNeighbour(neighbour_index, neighbour_point, INVALID_NEIGHBOUR);
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Remove tetrahedron
426
+ * Please note that if there are any dangling references in the mesh neighbourhood - you will need to take care of that separately
427
+ * @param {number} tetra_index
428
+ */
429
+ delete(tetra_index) {
430
+ assert.isNonNegativeInteger(tetra_index, 'tera_index');
431
+ assert.lessThan(tetra_index, this.__used_end, 'attempting to remove tet outside of valid region');
432
+ // assert.equal(this.__occupancy.get(tetra_index), true, 'tetrahedron does not exist');
433
+
434
+
435
+ if (tetra_index === this.__used_end - 1) {
436
+ // tet was at the end of the allocated space
437
+ this.__used_end--;
438
+ } else {
439
+ // mark as dead
440
+ this.__free[this.__free_pointer++] = tetra_index;
441
+ }
442
+
443
+ //assert(this.validateLength());
444
+
445
+ //assert(this.validateLength());
446
+ }
447
+
448
+ /**
449
+ * Used mainly to remove tetrahedrons whos points touch the "super-tetrahedron's" points that was inserted originally
450
+ * These points are identified by an offset + count parameters
451
+ * @param {number} range_start
452
+ * @param {number} range_end
453
+ */
454
+ removeTetrasConnectedToPoints(range_start, range_end) {
455
+
456
+ for (let i = this.__used_end - 1; i >= 0; i--) {
457
+
458
+ for (let j = 0; j < 4; j++) {
459
+ const point_index = this.getVertexIndex(i, j);
460
+
461
+ if (point_index >= range_start && point_index <= range_end) {
462
+
463
+ if (!this.exists(i)) {
464
+ // tet doesn't actually exist (deallocated)
465
+ break;
466
+ }
467
+
468
+ // point index is in range, tetra should be removed
469
+ this.disconnect(i);
470
+ this.delete(i);
471
+ break;
472
+ }
473
+ }
474
+ }
475
+
476
+ }
477
+
478
+ /**
479
+ *
480
+ * @param {number[]} points
481
+ * @param {number} index
482
+ * @returns {number} index of tetra or -1 if no containing tetra found
483
+ */
484
+ findTetraContainingPoint(points, index) {
485
+ const data_view = this.__view;
486
+
487
+ // TODO improve this
488
+ const n = this.__used_end;
489
+ for (let i = 0; i < n; i++) {
490
+
491
+ const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
492
+
493
+ const a = data_view.getUint32(tet_address);
494
+ const b = data_view.getUint32(tet_address + 4);
495
+ const c = data_view.getUint32(tet_address + 8);
496
+ const d = data_view.getUint32(tet_address + 12);
497
+
498
+ if (tetrahedron_contains_point(points, a, b, c, d, index)) {
499
+ return i;
500
+ }
501
+ }
502
+
503
+ return -1;
504
+ }
505
+
506
+
507
+ /**
508
+ * Relocate tetrahedron in memory, patches neighbourhood links as well
509
+ * NOTE: The destination slot will be overwritten. This is a dangerous method that can break the topology, make sure you fully understand what you are doing when using it
510
+ * @param {number} source_index index of source tetrahedron
511
+ * @param {number} destination_index new index, where the source tetrahedron is to be moved
512
+ */
513
+ relocate(source_index, destination_index) {
514
+ // validate_tetrahedron_neighbourhood(this, source_index, console.error);
515
+
516
+ // patch neighbours
517
+ for (let i = 0; i < 4; i++) {
518
+ const encoded_neighbour = this.getNeighbour(source_index, i);
519
+
520
+ if (encoded_neighbour === INVALID_NEIGHBOUR) {
521
+ // no neighbour
522
+ continue;
523
+ }
524
+
525
+ const neighbour_index = encoded_neighbour >> 2;
526
+ const neighbour_vertex = encoded_neighbour & 3;
527
+
528
+ const encoded_tet = (destination_index << 2) | (i & 3);
529
+
530
+ assert.equal(this.getNeighbour(neighbour_index, neighbour_vertex), (source_index << 2) | (i & 3), 'invalid source state');
531
+
532
+ this.setNeighbour(neighbour_index, neighbour_vertex, encoded_tet);
533
+ }
534
+
535
+
536
+ const layout_word_size = LAYOUT_TETRA_BYTE_SIZE >> 2;
537
+ array_copy(this.__data_uint32, source_index * layout_word_size, this.__data_uint32, destination_index * layout_word_size, layout_word_size);
538
+
539
+ // validate_tetrahedron_neighbourhood(this, destination_index, console.error);
540
+ }
541
+
542
+ /**
543
+ * Perform compaction, removing unused memory slots
544
+ * NOTE: existing tetrahedron indices can become invalidated as tets are moved into free slots
545
+ * @returns {number} number of relocated elements
546
+ */
547
+ compact() {
548
+ // sort free
549
+ array_quick_sort_by_comparator(this.__free, compareNumbersDescending, null, 0, this.__free_pointer - 1);
550
+
551
+ let relocation_count = 0;
552
+ let free_head_pointer = 0;
553
+
554
+ while (this.__free_pointer > free_head_pointer) {
555
+ const last_used = this.__used_end - 1;
556
+
557
+ if (this.__free[free_head_pointer] >= last_used) {
558
+ /*
559
+ The slot is actually free.
560
+ As we have sorted the free slots, it's expected to be at the start of the list
561
+ Adjust pointers and continue onto the next slot
562
+ */
563
+ free_head_pointer++
564
+ this.__used_end = last_used;
565
+ continue;
566
+ }
567
+
568
+ const free_slot = this.__free[this.__free_pointer - 1];
569
+
570
+ this.__free_pointer--;
571
+
572
+ if (last_used <= free_slot) {
573
+ continue;
574
+ }
575
+
576
+
577
+ this.relocate(last_used, free_slot);
578
+ relocation_count++;
579
+
580
+ this.__used_end--;
581
+
582
+ }
583
+
584
+ this.__free.splice(0, this.__free.length); // release memory
585
+ this.__free_pointer = 0;
586
+
587
+ return relocation_count;
588
+ }
589
+
590
+ /**
591
+ *
592
+ * @param {BinaryBuffer} buffer
593
+ */
594
+ serialize(buffer) {
595
+ buffer.writeUint32(1); // format version
596
+ buffer.writeUintVar(this.__used_end);
597
+ buffer.writeUintVar(this.__free_pointer);
598
+
599
+ // record main buffer
600
+ buffer.writeUint32Array(this.__data_uint32, 0, this.__used_end * LAYOUT_TETRA_WORD_COUNT);
601
+ buffer.writeUint32Array(this.__free, 0, this.__free_pointer);
602
+ }
603
+
604
+ /**
605
+ *
606
+ * @param {BinaryBuffer} buffer
607
+ */
608
+ deserialize(buffer) {
609
+ const version_number = buffer.readUint32();
610
+
611
+ if (version_number !== 1) {
612
+ throw new Error(`Unsupported version number, expected ${1}, instead got ${version_number}`);
613
+ }
614
+
615
+ this.__used_end = buffer.readUintVar();
616
+ this.__free_pointer = buffer.readUintVar();
617
+
618
+ this.growCapacity(this.__used_end);
619
+
620
+ buffer.readUint32Array(this.__data_uint32, 0, this.__used_end);
621
+
622
+ buffer.readUint32Array(this.__free, 0, this.__free_pointer);
623
+ }
624
+
625
+ /**
626
+ *
627
+ * @return {string}
628
+ */
629
+ serialize_base64() {
630
+ const buffer = new BinaryBuffer();
631
+
632
+ this.serialize(buffer);
633
+
634
+ buffer.trim();
635
+
636
+ return Base64.encode(buffer.data);
637
+ }
638
+
639
+ /**
640
+ *
641
+ * @param {string} str
642
+ */
643
+ deserialize_base64(str) {
644
+ const arrayBuffer = Base64.decode(str);
645
+
646
+ const binaryBuffer = BinaryBuffer.fromArrayBuffer(arrayBuffer);
647
+
648
+ this.deserialize(binaryBuffer);
649
+ }
650
+ }