@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.
- package/core/binary/BinaryBuffer.js +13 -1
- package/core/binary/BitSet.js +2 -2
- package/core/bvh2/aabb3/aabb3_array_combine.js +2 -2
- package/core/collection/RingBuffer.js +4 -2
- package/core/collection/RingBuffer.spec.js +59 -0
- package/core/collection/array/ArrayIteratorRandom.js +1 -1
- package/core/collection/{ArrayUtils.spec.js → array/arrayPickBestElement.spec.js} +1 -1
- package/core/collection/array/arrayPickBestElements.js +51 -0
- package/core/collection/array/arrayPickMinElement.js +43 -0
- package/core/collection/array/arrayQuickSort.js +1 -1
- package/core/collection/array/arraySetSortingDiff.js +1 -1
- package/core/collection/array/arraySwapElements.js +12 -0
- package/core/collection/array/array_range_equal_strict.js +22 -0
- package/core/collection/array/groupArrayBy.js +42 -0
- package/core/collection/array/isArrayEqual.js +50 -0
- package/core/collection/array/randomMultipleFromArray.js +34 -0
- package/core/collection/array/randomizeArrayElementOrder.js +23 -0
- package/core/color/sRGB_to_linear.js +9 -4
- package/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +1 -1
- package/core/geom/3d/aabb/aabb3_build_frustum.js +1 -1
- package/core/geom/3d/aabb/compute_aabb_from_points.js +1 -1
- package/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +3 -1
- package/core/geom/3d/morton/v3_morton_encode_transformed.spec.js +20 -0
- package/core/geom/3d/plane/orient3d_fast.js +11 -10
- package/core/geom/3d/plane/orient3d_robust.js +41 -0
- package/core/geom/3d/plane/plane_computeConvex3PlaneIntersection.js +0 -23
- package/core/geom/3d/plane/plane_three_compute_convex3_plane_intersection.js +24 -0
- package/core/geom/3d/shape/UnionShape3D.js +1 -1
- package/core/geom/3d/sphere/harmonics/README.md +15 -0
- package/core/geom/3d/sphere/harmonics/sh3_add.js +21 -0
- package/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +618 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +49 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js +53 -0
- package/core/geom/3d/tetrahedra/README.md +10 -1
- package/core/geom/3d/tetrahedra/TetrahedralMesh.js +650 -0
- package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +233 -0
- package/core/geom/3d/tetrahedra/build_tetrahedral_mesh_buffer_geometry.js +75 -0
- package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +2 -2
- package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.spec.js +4 -4
- package/core/geom/3d/tetrahedra/delaunay/Cavity.js +49 -7
- package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +51 -17
- package/core/geom/3d/tetrahedra/delaunay/debug_validate_mesh.js +19 -0
- package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +191 -0
- package/core/geom/3d/tetrahedra/delaunay/push_boundary_with_validation.js +27 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +59 -43
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.js +77 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.spec.js +30 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_towards_containing_tetrahedron.js +58 -0
- package/core/geom/3d/tetrahedra/delaunay/validate_cavity_boundary.js +60 -0
- package/core/geom/3d/tetrahedra/{point_in_tetrahedron_circumsphere.js → in_sphere_fast.js} +11 -13
- package/core/geom/3d/tetrahedra/in_sphere_robust.js +53 -0
- package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +44 -35
- package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.js +83 -0
- package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.spec.js +24 -0
- package/core/geom/3d/tetrahedra/tetrahedron_contains_point.spec.js +66 -0
- package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +166 -0
- package/core/geom/3d/util/make_justified_point_grid.js +31 -0
- package/core/geom/Bezier.js +0 -27
- package/core/geom/Plane.js +0 -4
- package/core/geom/packing/miniball/Subspan.js +2 -2
- package/core/geom/v3_lerp.js +6 -1
- package/core/math/isqrt.js +28 -0
- package/core/math/isqrt.spec.js +9 -0
- package/core/math/max.spec.js +25 -0
- package/core/math/min2.spec.js +25 -0
- package/core/model/node-graph/node/NodeInstance.js +3 -3
- package/core/primitives/strings/prefixTree/PrefixTreeLeaf.js +1 -1
- package/core/process/delay.js +5 -0
- package/core/process/task/util/randomCountTask.js +1 -1
- package/editor/Editor.js +3 -0
- package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +195 -11
- package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.js +16 -0
- package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +4 -0
- package/editor/ecs/component/editors/primitive/ArrayEditor.js +1 -1
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +6 -0
- package/editor/view/ecs/components/common/AutoCanvasView.js +13 -25
- package/engine/EngineHarness.js +11 -5
- package/engine/asset/AssetManager.d.ts +5 -1
- package/engine/asset/AssetManager.js +50 -15
- package/engine/asset/AssetManager.spec.js +17 -11
- package/engine/asset/AssetRequest.js +57 -0
- package/engine/asset/loaders/ArrayBufferLoader.js +22 -0
- package/engine/asset/loaders/AssetLoader.js +1 -1
- package/engine/ecs/System.js +1 -1
- package/engine/ecs/dynamic_actions/DynamicActorSystem.js +1 -1
- package/engine/ecs/terrain/ecs/TerrainSystem.js +7 -1
- package/engine/ecs/transform/copy_three_transform.js +15 -0
- package/engine/graphics/FrameRunner.js +5 -9
- package/engine/graphics/ecs/animation/animator/AnimationClipDefinition.js +1 -1
- package/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +1 -1
- package/engine/graphics/ecs/camera/Camera.js +1 -10
- package/engine/graphics/ecs/camera/CameraSystem.js +8 -8
- package/engine/graphics/ecs/camera/ProjectionType.js +9 -0
- package/engine/graphics/ecs/camera/build_three_camera_object.js +3 -3
- package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +59 -4
- package/engine/graphics/ecs/light/Light.js +6 -1
- package/engine/graphics/ecs/light/LightSystem.d.ts +1 -1
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +2 -17
- package/engine/graphics/geometry/VertexDataSpec.js +1 -1
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +2 -2
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +3 -3
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +3 -1
- package/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.spec.js +4 -0
- package/engine/graphics/render/forward_plus/computeFrustumCorners.js +4 -2
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
- package/engine/graphics/render/layers/RenderLayerUtils.js +2 -2
- package/engine/graphics/sh3/LightProbeVolume.js +595 -0
- package/engine/graphics/sh3/SH3VisualisationMaterial.js +79 -0
- package/engine/graphics/sh3/prototypeSH3Probe.js +427 -0
- package/engine/graphics/sh3/visualise_probe.js +40 -0
- package/engine/graphics/shaders/DenoiseShader.js +1 -1
- package/engine/graphics/texture/atlas/AtlasPatch.js +11 -3
- package/engine/graphics/texture/atlas/CachingTextureAtlas.js +2 -2
- package/engine/graphics/texture/atlas/TextureAtlas.js +22 -4
- package/engine/graphics/texture/atlas/TextureAtlas.spec.js +22 -0
- package/engine/graphics/texture/sampler/Sampler2D.js +0 -64
- package/engine/graphics/texture/sampler/Sampler2D.spec.js +2 -1
- package/engine/graphics/texture/sampler/sampler2d_combine.js +67 -0
- package/engine/intelligence/behavior/ecs/BehaviorSystem.spec.js +0 -3
- package/engine/intelligence/blackboard/AbstractBlackboard.d.ts +1 -1
- package/engine/network/PriorityFetch.js +192 -0
- package/engine/simulation/DormandPrince.js +1 -1
- package/engine/ui/DraggableAspect.js +0 -1
- package/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
- package/package.json +2 -1
- package/samples/terrain/from_image_2.js +127 -82
- package/view/elements/CanvasView.js +7 -1
- package/view/elements/image/HTMLElementCacheKey.js +1 -1
- package/view/util/DomSizeObserver.js +3 -5
- package/core/collection/ArrayUtils.js +0 -263
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +0 -48
- package/core/geom/3d/tetrahedra/hxt/a.js +0 -524
- package/core/geom/3d/tetrahedra/hxt/hxt.js +0 -140
- package/core/geom/3d/tetrahedra/hxt/hxt.wasm +0 -0
- 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[
|
|
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[
|
|
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,
|
|
26
|
-
expect(orient3d_fast(r,
|
|
27
|
-
expect(orient3d_fast(r,
|
|
28
|
-
expect(orient3d_fast(r,
|
|
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.
|
|
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.
|
|
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
|
-
|
|
25
|
-
|
|
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.
|
|
75
|
+
const n = this.__deleted_size;
|
|
35
76
|
|
|
36
77
|
for (let j = 0; j < n; j++) {
|
|
37
|
-
if (this.
|
|
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.
|
|
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 "../
|
|
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 {
|
|
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
|
-
|
|
43
|
+
// start with a fairly low capacity for best-case scenario, rely on internal resizing after that
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
|
58
|
+
create bounding volume tetrahedron over input set
|
|
41
59
|
*/
|
|
42
|
-
compute_bounding_simplex_3d(points, n * 3, points, n,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
+
mesh.removeTetrasConnectedToPoints(n, n + 3);
|
|
53
86
|
|
|
54
87
|
|
|
55
|
-
|
|
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
|
+
}
|