@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,233 @@
1
+ import { TetrahedralMesh } from "./TetrahedralMesh.js";
2
+ import { jest } from "@jest/globals";
3
+
4
+ test("constructor does not throw", () => {
5
+ expect(() => new TetrahedralMesh(1)).not.toThrow();
6
+ });
7
+
8
+ test("capacity set in constructor is respected", () => {
9
+ const mesh = new TetrahedralMesh(7);
10
+
11
+ expect(mesh.getCapacity()).toBe(7);
12
+ });
13
+
14
+ test("attempting to set capacity below what's used will throw", () => {
15
+ const mesh = new TetrahedralMesh(1);
16
+
17
+ mesh.allocate();
18
+ mesh.allocate();
19
+
20
+ expect(() => mesh.setCapacity(1)).toThrow();
21
+ });
22
+
23
+ test("set/get capacity consistence", () => {
24
+ const mesh = new TetrahedralMesh(2);
25
+
26
+ mesh.setCapacity(1);
27
+
28
+ expect(mesh.getCapacity()).toBe(1);
29
+
30
+ mesh.setCapacity(1); // idempotency check
31
+
32
+ expect(mesh.getCapacity()).toBe(1);
33
+
34
+ mesh.setCapacity(2);
35
+
36
+ expect(mesh.getCapacity()).toBe(2);
37
+ });
38
+
39
+
40
+ test("set/get neighbour consistency", () => {
41
+ const mesh = new TetrahedralMesh(7);
42
+
43
+ const tet = mesh.allocate();
44
+
45
+ mesh.setNeighbour(tet, 0, 7);
46
+ mesh.setNeighbour(tet, 1, 3);
47
+ mesh.setNeighbour(tet, 2, 11);
48
+ mesh.setNeighbour(tet, 3, 13);
49
+
50
+ //
51
+ expect(mesh.getNeighbour(tet, 0)).toBe(7);
52
+ expect(mesh.getNeighbour(tet, 1)).toBe(3);
53
+ expect(mesh.getNeighbour(tet, 2)).toBe(11);
54
+ expect(mesh.getNeighbour(tet, 3)).toBe(13);
55
+ });
56
+
57
+ test("set/get vertex index consistency", () => {
58
+ const mesh = new TetrahedralMesh(7);
59
+ const tet = mesh.allocate();
60
+
61
+ mesh.setVertexIndex(tet, 0, 3);
62
+ mesh.setVertexIndex(tet, 1, 7);
63
+ mesh.setVertexIndex(tet, 2, 11);
64
+ mesh.setVertexIndex(tet, 3, 13);
65
+
66
+ //
67
+ expect(mesh.getVertexIndex(tet, 0)).toBe(3);
68
+ expect(mesh.getVertexIndex(tet, 1)).toBe(7);
69
+ expect(mesh.getVertexIndex(tet, 2)).toBe(11);
70
+ expect(mesh.getVertexIndex(tet, 3)).toBe(13);
71
+ });
72
+
73
+ test("allocation within capacity doesn't resize capacity", () => {
74
+ const mesh = new TetrahedralMesh(1);
75
+
76
+ expect(mesh.getCapacity()).toBe(1);
77
+
78
+ mesh.allocate();
79
+
80
+ expect(mesh.getCapacity()).toBe(1); // allocation fits in the capacity, so no growth is expected
81
+ });
82
+
83
+ test("allocation beyond capacity does not destroy existing data", () => {
84
+ const mesh = new TetrahedralMesh(1);
85
+
86
+ const t0 = mesh.allocate();
87
+
88
+ expect(mesh.getCapacity()).toBe(1);
89
+
90
+ for (let i = 0; i < 4; i++) {
91
+ mesh.setNeighbour(t0, i, 1);
92
+ mesh.setVertexIndex(t0, i, 3);
93
+ }
94
+
95
+ mesh.allocate(); // allocation over capacity
96
+
97
+ expect(mesh.getCapacity()).toBeGreaterThan(1);
98
+
99
+ for (let i = 0; i < 4; i++) {
100
+ expect(mesh.getNeighbour(t0, i)).toBe(1);
101
+ expect(mesh.getVertexIndex(t0, i)).toBe(3);
102
+ }
103
+
104
+ });
105
+
106
+ test("allocation increases size", () => {
107
+ const mesh = new TetrahedralMesh(1);
108
+
109
+ expect(mesh.size()).toBe(0);
110
+
111
+ mesh.allocate();
112
+
113
+ expect(mesh.size()).toBe(1);
114
+ });
115
+
116
+ test("remove method reduces size", () => {
117
+ const mesh = new TetrahedralMesh(1);
118
+
119
+ const tet = mesh.allocate();
120
+
121
+ expect(mesh.size()).toBe(1);
122
+
123
+ mesh.delete(tet);
124
+
125
+ expect(mesh.size()).toBe(0);
126
+ });
127
+
128
+
129
+ test("forEach visits all allocated tets", () => {
130
+ const mesh = new TetrahedralMesh();
131
+
132
+ const t0 = mesh.allocate();
133
+ const t1 = mesh.allocate();
134
+
135
+ const mock = jest.fn();
136
+
137
+ mesh.forEach(mock);
138
+
139
+ expect(mock).toHaveBeenCalledTimes(2);
140
+ expect(mock).toHaveBeenCalledWith(t0, mesh);
141
+ expect(mock).toHaveBeenCalledWith(t1, mesh);
142
+ });
143
+
144
+ test("'exists' method", () => {
145
+ const mesh = new TetrahedralMesh();
146
+
147
+ expect(mesh.exists(0)).toBe(false);
148
+
149
+ const tet = mesh.allocate();
150
+
151
+ expect(mesh.exists(tet)).toBe(true);
152
+ expect(mesh.exists(tet + 1)).toBe(false);
153
+
154
+ mesh.delete(tet);
155
+
156
+ expect(mesh.exists(tet)).toBe(false);
157
+ expect(mesh.exists(tet + 1)).toBe(false);
158
+ });
159
+
160
+
161
+ test("after calling 'removeTetrasConnectedToPoints' matching points are no logner indexed by live tetrahedrons", () => {
162
+
163
+
164
+ const tetrahedra = new TetrahedralMesh();
165
+
166
+ /*
167
+ * This "magic" mesh is a result of tetrahedral mesh generator with following input: 0, 0, 0, 10, 0, 0, 10, 0, 10, 0, 0, 10, 0, 10, 0,
168
+ * The mesh was generated on 27/10/2022
169
+ * It's a very small example with 21 total tets that was failing this test
170
+ *
171
+ */
172
+ tetrahedra.deserialize_base64('AAAAARUAAQAAAAAAAAAFAAAABgAAAAsAAAAUAAAAHwAAADQAAAADAAAABwAAAAUAAAAIAAAA/////yoAAABAAAAALgAAAAAAAAAFAAAABgAAAAgAAAD/////HAAAACgAAAAAAAAAAgAAAAUAAAAHAAAABgAAAP////8TAAAAFQAAACwAAAACAAAABgAAAAcAAAAIAAAA/////zwAAAAwAAAADQAAAAIAAAABAAAABQAAAAYAAAABAAAADgAAACAAAAA4AAAABAAAAAEAAAAGAAAACAAAAB0AAAAyAAAASQAAACEAAAABAAAAAAAAAAYAAAAIAAAACQAAABgAAABIAAAAAgAAAAQAAAACAAAABgAAAAEAAAAWAAAAGwAAACUAAAAzAAAABAAAAAMAAAACAAAAAQAAADsAAAAiAAAAUwAAAEYAAAADAAAABQAAAAAAAAAIAAAACgAAAEwAAAAFAAAANQAAAAMAAAAHAAAAAgAAAAUAAAAPAAAAOgAAAAcAAABEAAAABAAAAAYAAAACAAAACAAAABIAAAA+AAAAGQAAACMAAAADAAAAAQAAAAAAAAAFAAAAAwAAACsAAAA5AAAAUAAAAAMAAAACAAAAAQAAAAUAAAAXAAAANgAAAC0AAAAkAAAABAAAAAIAAAAHAAAACAAAABEAAABCAAAAMQAAAEUAAAAEAAAABwAAAAMAAAAIAAAABgAAAE4AAAA9AAAARwAAAAQAAAADAAAABwAAAAIAAAAvAAAAPwAAACcAAABDAAAABAAAAAAAAAABAAAACAAAAB4AAAAaAAAATQAAAFEAAAAEAAAAAwAAAAAAAAAIAAAAKQAAAEoAAABBAAAAUgAAAAQAAAADAAAAAQAAAAAAAAA3AAAASwAAAE8AAAAmAAAA');
173
+
174
+ tetrahedra.removeTetrasConnectedToPoints(5, 8);
175
+
176
+ //The mesh should contain only 5 vertices
177
+
178
+ tetrahedra.forEach((tet, mesh) => {
179
+
180
+ for (let i = 0; i < 4; i++) {
181
+ expect(mesh.getVertexIndex(tet, i)).toBeLessThan(5);
182
+ }
183
+
184
+ });
185
+ });
186
+
187
+ test('relocate operation copies tet data and updates back-links correctly', () => {
188
+ const mesh = new TetrahedralMesh();
189
+
190
+ const a = mesh.allocate();
191
+ const b = mesh.allocate();
192
+ const dest = mesh.allocate();
193
+
194
+ mesh.setVertexIndex(a, 0, 3);
195
+ mesh.setVertexIndex(a, 1, 7);
196
+ mesh.setVertexIndex(a, 2, 11);
197
+ mesh.setVertexIndex(a, 3, 13);
198
+
199
+ mesh.setNeighbour(a, 0, b << 2);
200
+ mesh.setNeighbour(b, 0, a << 2);
201
+
202
+ mesh.relocate(a, dest);
203
+
204
+ expect(mesh.getVertexIndex(dest, 0)).toBe(3);
205
+ expect(mesh.getVertexIndex(dest, 1)).toBe(7);
206
+ expect(mesh.getVertexIndex(dest, 2)).toBe(11);
207
+ expect(mesh.getVertexIndex(dest, 3)).toBe(13);
208
+
209
+ expect(mesh.getNeighbour(dest, 0)).toBe(b << 2);
210
+
211
+ // check back-link
212
+ expect(mesh.getNeighbour(b, 0)).toBe(dest << 2);
213
+ });
214
+
215
+ test('Compaction is non-destructive when moving resident data from the very end of the allocated region', () => {
216
+ const mesh = new TetrahedralMesh();
217
+
218
+ const a = mesh.allocate();
219
+ const b = mesh.allocate();
220
+
221
+ // force move "a" to free set
222
+ mesh.__free[mesh.__free_pointer++] = a;
223
+
224
+ expect(mesh.exists(b)).toBe(true);
225
+ expect(mesh.exists(a)).toBe(false);
226
+
227
+ mesh.compact();
228
+
229
+ expect(mesh.size()).toBe(1);
230
+
231
+ expect(mesh.exists(0)).toBe(true);
232
+ expect(mesh.exists(1)).toBe(false);
233
+ });
@@ -0,0 +1,75 @@
1
+ import { BufferGeometry, Float32BufferAttribute, Uint32BufferAttribute } from "three";
2
+ import { BitSet } from "../../../binary/BitSet.js";
3
+ import { assert } from "../../../assert.js";
4
+
5
+ /**
6
+ *
7
+ * @param {TetrahedralMesh} tetrahedra
8
+ * @param {Float32Array|number[]} points
9
+ * @return {BufferGeometry}
10
+ */
11
+ export function build_tetrahedral_mesh_buffer_geometry(tetrahedra, points) {
12
+ const lines = [];
13
+
14
+ const occupancy = new BitSet();
15
+
16
+ const point_count = points.length / 3;
17
+
18
+ assert.isNonNegativeInteger(point_count, 'point_count');
19
+
20
+ /**
21
+ *
22
+ * @param {number} a
23
+ * @param {number} b
24
+ */
25
+ function ensure_line(a, b) {
26
+ assert.isNonNegativeInteger(a, 'a');
27
+ assert.isNonNegativeInteger(b, 'b');
28
+
29
+ assert.lessThan(a, point_count, 'index A is out of bounds');
30
+ assert.lessThan(b, point_count, 'index B is out of bounds');
31
+
32
+ let v0, v1;
33
+
34
+ if (a < b) {
35
+ v0 = a;
36
+ v1 = b;
37
+ } else {
38
+ v0 = b;
39
+ v1 = a;
40
+ }
41
+
42
+ const index = v0 * point_count + v1;
43
+
44
+ if (occupancy.getAndSet(index)) {
45
+ // already recorded
46
+ return;
47
+ }
48
+
49
+ // no line record yet, create it
50
+ lines.push(v0, v1);
51
+ }
52
+
53
+ tetrahedra.forEach((tet, mesh) => {
54
+ assert.ok(mesh.exists(tet), `Visited un-allocated tet ${tet}`);
55
+
56
+ const a = mesh.getVertexIndex(tet, 0);
57
+ const b = mesh.getVertexIndex(tet, 1);
58
+ const c = mesh.getVertexIndex(tet, 2);
59
+ const d = mesh.getVertexIndex(tet, 3);
60
+
61
+ ensure_line(a, b);
62
+ ensure_line(a, c);
63
+ ensure_line(a, d);
64
+
65
+ ensure_line(b, c);
66
+ ensure_line(c, d);
67
+ ensure_line(b, d);
68
+ })
69
+
70
+ const geometry = new BufferGeometry();
71
+
72
+ geometry.setIndex(new Uint32BufferAttribute(lines, 1, false));
73
+ geometry.setAttribute('position', new Float32BufferAttribute(points, 3, false));
74
+ return geometry;
75
+ }
@@ -156,7 +156,7 @@ export function compute_bounding_simplex_3d(
156
156
  result, result_offset,
157
157
  SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
158
158
  SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
159
- SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
159
+ SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
160
160
  );
161
161
  plane_computeConvex3PlaneIntersection(
162
162
  result, result_offset + 3,
@@ -168,7 +168,7 @@ export function compute_bounding_simplex_3d(
168
168
  result, result_offset + 6,
169
169
  SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
170
170
  SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
171
- SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
171
+ SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
172
172
  );
173
173
  plane_computeConvex3PlaneIntersection(
174
174
  result, result_offset + 9,
@@ -22,10 +22,10 @@ test('planar containment, one point', () => {
22
22
 
23
23
  r.push(0, 0, 0);
24
24
 
25
- expect(orient3d_fast(r, 1, 3, 2, 4)).toBeLessThan(0);
26
- expect(orient3d_fast(r, 2, 3, 0, 4)).toBeLessThan(0);
27
- expect(orient3d_fast(r, 3, 1, 0, 4)).toBeLessThan(0);
28
- expect(orient3d_fast(r, 0, 1, 2, 4)).toBeLessThan(0);
25
+ expect(orient3d_fast(r, 2, 3, 1, 4)).toBeLessThan(0);
26
+ expect(orient3d_fast(r, 0, 3, 2, 4)).toBeLessThan(0);
27
+ expect(orient3d_fast(r, 0, 1, 3, 4)).toBeLessThan(0);
28
+ expect(orient3d_fast(r, 2, 1, 0, 4)).toBeLessThan(0);
29
29
  });
30
30
 
31
31
  test('positive padding results in larger simplex', () => {
@@ -1,16 +1,55 @@
1
+ import { assert } from "../../../../assert.js";
2
+
1
3
  export class Cavity {
2
4
  constructor() {
3
5
  /**
4
6
  *
5
7
  * @type {number[]}
6
8
  */
7
- this.indices = [];
9
+ this.__deleted = [];
10
+
11
+ /**
12
+ *
13
+ * @type {number}
14
+ */
15
+ this.__deleted_size = 0;
16
+
17
+ /**
18
+ * flat array of 5-tuples
19
+ * @type {number[]}
20
+ * @private
21
+ */
22
+ this.__boundary = [];
8
23
 
9
24
  /**
10
25
  *
11
26
  * @type {number}
27
+ * @private
12
28
  */
13
- this.size = 0;
29
+ this.__boundary_size = 0;
30
+ }
31
+
32
+ /**
33
+ * Add boundary information
34
+ * @param vta
35
+ * @param node1
36
+ * @param node2
37
+ * @param node3
38
+ * @param bnd
39
+ */
40
+ push_boundary(vta, node1, node2, node3, bnd) {
41
+
42
+ const n = this.__boundary_size;
43
+
44
+ const n5 = n * 5;
45
+
46
+ this.__boundary[n5] = vta;
47
+ this.__boundary[n5 + 1] = node1;
48
+ this.__boundary[n5 + 2] = node2;
49
+ this.__boundary[n5 + 3] = node3;
50
+ this.__boundary[n5 + 4] = bnd;
51
+
52
+ this.__boundary_size++;
14
53
  }
15
54
 
16
55
  update() {
@@ -21,8 +60,10 @@ export class Cavity {
21
60
  *
22
61
  * @param {number} i
23
62
  */
24
- push(i) {
25
- this.indices[this.size++] = i;
63
+ push_deleted(i) {
64
+ assert.equal(this.includes(i), false, `ball element ${i} already exists`);
65
+
66
+ this.__deleted[this.__deleted_size++] = i;
26
67
  }
27
68
 
28
69
  /**
@@ -31,10 +72,10 @@ export class Cavity {
31
72
  * @returns {boolean}
32
73
  */
33
74
  includes(i) {
34
- const n = this.size;
75
+ const n = this.__deleted_size;
35
76
 
36
77
  for (let j = 0; j < n; j++) {
37
- if (this.indices[j] === i) {
78
+ if (this.__deleted[j] === i) {
38
79
  return true;
39
80
  }
40
81
  }
@@ -43,6 +84,7 @@ export class Cavity {
43
84
  }
44
85
 
45
86
  reset() {
46
- this.size = 0;
87
+ this.__deleted_size = 0;
88
+ this.__boundary_size = 0;
47
89
  }
48
90
  }
@@ -1,16 +1,35 @@
1
1
  //
2
2
 
3
3
  import { compute_bounding_simplex_3d } from "../compute_bounding_simplex_3d.js";
4
- import { TetrahedralMesh } from "../tetrahedra_collection.js";
4
+ import { TetrahedralMesh } from "../TetrahedralMesh.js";
5
+ import {
6
+ tetrahedral_mesh_walk_towards_containing_tetrahedron
7
+ } from "./tetrahedral_mesh_walk_towards_containing_tetrahedron.js";
8
+ import { Cavity } from "./Cavity.js";
9
+ import { tetrahedral_mesh_compute_cavity } from "./tetrahedral_mesh_compute_cavity.js";
10
+ import { fill_in_a_cavity } from "./fill_in_a_cavity.js";
11
+ import { BitSet } from "../../../../binary/BitSet.js";
12
+ import { assert } from "../../../../assert.js";
13
+
5
14
 
6
15
  /**
7
16
  * @see https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
8
17
  * @see [1] "One machine, one minute, three billion tetrahedra" by Célestin Marot et al
18
+ * @param {TetrahedralMesh} mesh
9
19
  * @param {number[]} input serialized set of 3d points (x,y,z)
10
20
  * @param {number} n number of points in the input
11
- * @returns {TetrahedralMesh}
21
+ * @returns {boolean}
12
22
  */
13
- export function compute_delaunay_tetrahedral_mesh(input, n) {
23
+ export function compute_delaunay_tetrahedral_mesh(mesh, input, n) {
24
+ assert.notNull(mesh, 'mesh');
25
+ assert.defined(mesh, 'mesh');
26
+
27
+ assert.isNonNegativeInteger(n, 'n');
28
+
29
+ if (n < 4) {
30
+ // no mesh can be created, too few points
31
+ return false;
32
+ }
14
33
 
15
34
  /**
16
35
  * According to [1] number of tetrahedra is around 6 times greater than number of input vertices, we use that fact to pre-allocate memory
@@ -20,39 +39,54 @@ export function compute_delaunay_tetrahedral_mesh(input, n) {
20
39
  *
21
40
  * The +7 is for the initial "super" tetrahedron and 6 tetras for intermediate triangulation
22
41
  */
23
- const tetrahedron_count_upper_bound = Math.ceil((n * n - 3 * n - 2) / 2) + 7;
24
42
 
25
- const tetrahedrons = new TetrahedralMesh(tetrahedron_count_upper_bound);
43
+ // start with a fairly low capacity for best-case scenario, rely on internal resizing after that
26
44
 
27
- if (n < 4) {
28
- // no mesh can be created, too few points
29
- return tetrahedrons;
30
- }
45
+ mesh.growCapacity(n + 1);
46
+
47
+ const CAVITY = new Cavity();
31
48
 
32
49
  // TODO apply spatial sort to point, to get better cache locality
33
50
 
34
- // compute bounds over the input
51
+ const build_flags = BitSet.fixedSize(n);
52
+
35
53
  const points = new Float32Array((n + 4) * 3);
36
54
 
37
55
  points.set(input);
38
56
 
39
57
  /*
40
- create super bounding volume
58
+ create bounding volume tetrahedron over input set
41
59
  */
42
- compute_bounding_simplex_3d(points, n * 3, points, n, 1);
60
+ compute_bounding_simplex_3d(points, n * 3, points, n, 10);
61
+
62
+ const super_tet = mesh.allocate();
63
+
64
+ mesh.setVertexIndex(super_tet, 0, n);
65
+ mesh.setVertexIndex(super_tet, 1, n + 1);
66
+ mesh.setVertexIndex(super_tet, 2, n + 2);
67
+ mesh.setVertexIndex(super_tet, 3, n + 3);
43
68
 
44
- tetrahedrons.append(points, n, n + 1, n + 2, n + 3);
69
+ // debug_validate_mesh(mesh, points);
45
70
 
46
71
  // add all points to the mesh
72
+ let current_tet = 0;
73
+
47
74
  for (let i = 0; i < n; i++) {
48
- tetrahedrons.insertPoint(points, i);
75
+
76
+ current_tet = tetrahedral_mesh_walk_towards_containing_tetrahedron(mesh, points, current_tet, i);
77
+
78
+ tetrahedral_mesh_compute_cavity(mesh, points, build_flags, CAVITY, current_tet, i);
79
+
80
+ current_tet = fill_in_a_cavity(mesh, points, build_flags, CAVITY, current_tet);
81
+
49
82
  }
50
83
 
51
84
  // remove tetras formed by with the super bounding volume
52
- tetrahedrons.removeTetrasConnectedToPoints(points, n, n + 3);
85
+ mesh.removeTetrasConnectedToPoints(n, n + 3);
53
86
 
54
87
 
55
- return tetrahedrons;
56
- }
88
+ // debug_validate_mesh(mesh, points);
57
89
 
90
+ return true;
91
+ }
58
92
 
@@ -0,0 +1,19 @@
1
+ import { validate_tetrahedral_mesh } from "../validate_tetrahedral_mesh.js";
2
+
3
+ /**
4
+ *
5
+ * @param {TetrahedralMesh} mesh
6
+ * @param {ArrayLike<number>|Float32Array} points
7
+ */
8
+ export function debug_validate_mesh(mesh, points) {
9
+ const problems = [];
10
+
11
+ function add(x) {
12
+ problems.push(x);
13
+ }
14
+
15
+ if (!validate_tetrahedral_mesh(mesh, points, add)) {
16
+ debugger;
17
+ console.error(problems.join('\n'));
18
+ }
19
+ }