@woosh/meep-engine 2.43.0 → 2.43.3

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 (105) hide show
  1. package/core/bvh2/aabb3/aabb3_array_combine.js +2 -2
  2. package/core/collection/RingBuffer.js +4 -2
  3. package/core/collection/RingBuffer.spec.js +59 -0
  4. package/core/collection/array/ArrayIteratorRandom.js +1 -1
  5. package/core/collection/{ArrayUtils.spec.js → array/arrayPickBestElement.spec.js} +1 -1
  6. package/core/collection/array/arrayPickBestElements.js +51 -0
  7. package/core/collection/array/arrayPickMinElement.js +43 -0
  8. package/core/collection/array/arrayQuickSort.js +1 -1
  9. package/core/collection/array/arraySetSortingDiff.js +1 -1
  10. package/core/collection/array/arraySwapElements.js +12 -0
  11. package/core/collection/array/groupArrayBy.js +42 -0
  12. package/core/collection/array/isArrayEqual.js +50 -0
  13. package/core/collection/array/randomMultipleFromArray.js +34 -0
  14. package/core/collection/array/randomizeArrayElementOrder.js +23 -0
  15. package/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +1 -1
  16. package/core/geom/3d/aabb/aabb3_build_frustum.js +1 -1
  17. package/core/geom/3d/aabb/compute_aabb_from_points.js +1 -1
  18. package/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +3 -1
  19. package/core/geom/3d/morton/v3_morton_encode_transformed.spec.js +20 -0
  20. package/core/geom/3d/plane/orient3d_fast.js +8 -10
  21. package/core/geom/3d/plane/plane_computeConvex3PlaneIntersection.js +0 -23
  22. package/core/geom/3d/plane/plane_three_compute_convex3_plane_intersection.js +24 -0
  23. package/core/geom/3d/shape/UnionShape3D.js +1 -1
  24. package/core/geom/3d/tetrahedra/README.md +10 -1
  25. package/core/geom/3d/tetrahedra/{tetrahedra_collection.js → TetrahedralMesh.js} +236 -152
  26. package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +156 -0
  27. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +2 -2
  28. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.spec.js +4 -4
  29. package/core/geom/3d/tetrahedra/delaunay/Cavity.js +45 -7
  30. package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +44 -9
  31. package/core/geom/3d/tetrahedra/delaunay/debug_validate_mesh.js +19 -0
  32. package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +155 -0
  33. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity2.js +224 -0
  34. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.js +77 -0
  35. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_sub_determinant.spec.js +30 -0
  36. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_insert_point.js +98 -0
  37. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +13 -6
  38. package/core/geom/3d/tetrahedra/point_in_tetrahedron_circumsphere.js +9 -9
  39. package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +1 -1
  40. package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.js +83 -0
  41. package/core/geom/3d/tetrahedra/tetrahedron_compute_signed_volume.spec.js +24 -0
  42. package/core/geom/3d/tetrahedra/tetrahedron_contains_point.spec.js +66 -0
  43. package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +119 -0
  44. package/core/geom/Bezier.js +0 -27
  45. package/core/geom/Plane.js +0 -4
  46. package/core/geom/packing/miniball/Subspan.js +2 -2
  47. package/core/geom/v3_lerp.js +6 -1
  48. package/core/math/isqrt.js +28 -0
  49. package/core/math/isqrt.spec.js +9 -0
  50. package/core/math/max.spec.js +25 -0
  51. package/core/math/min2.spec.js +25 -0
  52. package/core/model/node-graph/node/NodeInstance.js +3 -3
  53. package/core/primitives/strings/prefixTree/PrefixTreeLeaf.js +1 -1
  54. package/core/process/task/util/randomCountTask.js +1 -1
  55. package/editor/ecs/component/editors/primitive/ArrayEditor.js +1 -1
  56. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +6 -0
  57. package/editor/view/ecs/components/common/AutoCanvasView.js +13 -25
  58. package/engine/asset/AssetManager.d.ts +7 -1
  59. package/engine/asset/AssetManager.js +50 -15
  60. package/engine/asset/AssetManager.spec.js +17 -11
  61. package/engine/asset/AssetRequest.js +57 -0
  62. package/engine/asset/loaders/ArrayBufferLoader.js +22 -0
  63. package/engine/asset/loaders/AssetLoader.js +1 -1
  64. package/engine/ecs/System.js +1 -1
  65. package/engine/ecs/dynamic_actions/DynamicActorSystem.js +1 -1
  66. package/engine/graphics/FrameRunner.js +5 -9
  67. package/engine/graphics/ecs/animation/animator/AnimationClipDefinition.js +1 -1
  68. package/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +1 -1
  69. package/engine/graphics/ecs/camera/Camera.js +1 -10
  70. package/engine/graphics/ecs/camera/CameraSystem.js +8 -8
  71. package/engine/graphics/ecs/camera/ProjectionType.js +9 -0
  72. package/engine/graphics/ecs/camera/build_three_camera_object.js +3 -3
  73. package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +59 -4
  74. package/engine/graphics/geometry/VertexDataSpec.js +1 -1
  75. package/engine/graphics/impostors/octahedral/prototypeBaker.js +3 -3
  76. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
  77. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +3 -1
  78. package/engine/graphics/particles/node-based/codegen/modules/FunctionSignature.js +1 -1
  79. package/engine/graphics/render/forward_plus/LightManager.js +1 -1
  80. package/engine/graphics/render/forward_plus/LightManager.spec.js +4 -0
  81. package/engine/graphics/render/forward_plus/computeFrustumCorners.js +4 -2
  82. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
  83. package/engine/graphics/render/layers/RenderLayerUtils.js +2 -2
  84. package/engine/graphics/shaders/DenoiseShader.js +1 -1
  85. package/engine/graphics/texture/atlas/AtlasPatch.js +11 -3
  86. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +2 -2
  87. package/engine/graphics/texture/atlas/TextureAtlas.js +7 -1
  88. package/engine/graphics/texture/atlas/TextureAtlas.spec.js +22 -0
  89. package/engine/graphics/texture/sampler/Sampler2D.js +0 -64
  90. package/engine/graphics/texture/sampler/Sampler2D.spec.js +2 -1
  91. package/engine/graphics/texture/sampler/sampler2d_combine.js +67 -0
  92. package/engine/intelligence/behavior/ecs/BehaviorSystem.spec.js +0 -3
  93. package/engine/network/PriorityFetch.js +192 -0
  94. package/engine/simulation/DormandPrince.js +1 -1
  95. package/engine/ui/DraggableAspect.js +0 -1
  96. package/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
  97. package/package.json +1 -1
  98. package/view/elements/CanvasView.js +7 -1
  99. package/view/elements/image/HTMLElementCacheKey.js +1 -1
  100. package/view/util/DomSizeObserver.js +3 -5
  101. package/core/collection/ArrayUtils.js +0 -263
  102. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +0 -73
  103. package/core/geom/3d/tetrahedra/hxt/a.js +0 -524
  104. package/core/geom/3d/tetrahedra/hxt/hxt.js +0 -140
  105. package/core/geom/3d/tetrahedra/hxt/hxt.wasm +0 -0
@@ -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', () => {
@@ -4,13 +4,50 @@ export class Cavity {
4
4
  *
5
5
  * @type {number[]}
6
6
  */
7
- this.indices = [];
7
+ this.__deleted = [];
8
8
 
9
9
  /**
10
10
  *
11
11
  * @type {number}
12
12
  */
13
- this.size = 0;
13
+ this.__deleted_size = 0;
14
+
15
+ /**
16
+ * 5-tuples
17
+ * @type {number[]}
18
+ * @private
19
+ */
20
+ this.__boundary = [];
21
+
22
+ /**
23
+ *
24
+ * @type {number}
25
+ * @private
26
+ */
27
+ this.__boundary_size = 0;
28
+ }
29
+
30
+ /**
31
+ * Add boundary information
32
+ * @param vta
33
+ * @param node1
34
+ * @param node2
35
+ * @param node3
36
+ * @param bnd
37
+ */
38
+ push_boundary(vta, node1, node2, node3, bnd) {
39
+
40
+ const n = this.__boundary_size;
41
+
42
+ const n5 = n * 5;
43
+
44
+ this.__boundary[n5] = vta;
45
+ this.__boundary[n5 + 1] = node1;
46
+ this.__boundary[n5 + 2] = node2;
47
+ this.__boundary[n5 + 3] = node3;
48
+ this.__boundary[n5 + 4] = bnd;
49
+
50
+ this.__boundary_size++;
14
51
  }
15
52
 
16
53
  update() {
@@ -21,8 +58,8 @@ export class Cavity {
21
58
  *
22
59
  * @param {number} i
23
60
  */
24
- push(i) {
25
- this.indices[this.size++] = i;
61
+ push_deleted(i) {
62
+ this.__deleted[this.__deleted_size++] = i;
26
63
  }
27
64
 
28
65
  /**
@@ -31,10 +68,10 @@ export class Cavity {
31
68
  * @returns {boolean}
32
69
  */
33
70
  includes(i) {
34
- const n = this.size;
71
+ const n = this.__deleted_size;
35
72
 
36
73
  for (let j = 0; j < n; j++) {
37
- if (this.indices[j] === i) {
74
+ if (this.__deleted[j] === i) {
38
75
  return true;
39
76
  }
40
77
  }
@@ -43,6 +80,7 @@ export class Cavity {
43
80
  }
44
81
 
45
82
  reset() {
46
- this.size = 0;
83
+ this.__deleted_size = 0;
84
+ this.__boundary_size = 0;
47
85
  }
48
86
  }
@@ -1,7 +1,14 @@
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 { tetrahedral_mesh_walk_toward_cavity } from "./tetrahedral_mesh_walk_toward_cavity.js";
6
+ import { tetrahedral_mesh_compute_sub_determinant } from "./tetrahedral_mesh_compute_sub_determinant.js";
7
+ import { Cavity } from "./Cavity.js";
8
+ import { tetrahedral_mesh_compute_cavity2 } from "./tetrahedral_mesh_compute_cavity2.js";
9
+ import { fill_in_a_cavity } from "./fill_in_a_cavity.js";
10
+ import { debug_validate_mesh } from "./debug_validate_mesh.js";
11
+
5
12
 
6
13
  /**
7
14
  * @see https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
@@ -22,37 +29,65 @@ export function compute_delaunay_tetrahedral_mesh(input, n) {
22
29
  */
23
30
  const tetrahedron_count_upper_bound = Math.ceil((n * n - 3 * n - 2) / 2) + 7;
24
31
 
25
- const tetrahedrons = new TetrahedralMesh(tetrahedron_count_upper_bound);
32
+ const mesh = new TetrahedralMesh(tetrahedron_count_upper_bound);
26
33
 
27
34
  if (n < 4) {
28
35
  // no mesh can be created, too few points
29
- return tetrahedrons;
36
+ return mesh;
30
37
  }
31
38
 
39
+ const CAVITY = new Cavity();
40
+
32
41
  // TODO apply spatial sort to point, to get better cache locality
33
42
 
34
43
  // compute bounds over the input
44
+ const sub_determinants = new Float32Array(tetrahedron_count_upper_bound * 4);
35
45
  const points = new Float32Array((n + 4) * 3);
36
46
 
37
47
  points.set(input);
38
48
 
39
49
  /*
40
- create super bounding volume
50
+ create bounding volume tetrahedron over input set
41
51
  */
42
52
  compute_bounding_simplex_3d(points, n * 3, points, n, 1);
43
53
 
44
- tetrahedrons.append(points, n, n + 1, n + 2, n + 3);
54
+ const super_tet = mesh.allocate();
55
+
56
+ mesh.setVertexIndex(super_tet, 0, n);
57
+ mesh.setVertexIndex(super_tet, 1, n + 1);
58
+ mesh.setVertexIndex(super_tet, 2, n + 2);
59
+ mesh.setVertexIndex(super_tet, 3, n + 3);
60
+
61
+ // make circular references on neighbour (algorithm doesn't work otherwise)
62
+ // mesh.setNeighbour(super_tet, 0, super_tet << 2 | 0);
63
+ // mesh.setNeighbour(super_tet, 1, super_tet << 2 | 1);
64
+ // mesh.setNeighbour(super_tet, 2, super_tet << 2 | 2);
65
+ // mesh.setNeighbour(super_tet, 3, super_tet << 2 | 3);
66
+
67
+ tetrahedral_mesh_compute_sub_determinant(sub_determinants, 0, mesh, points, 0);
68
+
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_toward_cavity(mesh, points, current_tet, i);
77
+
78
+ tetrahedral_mesh_compute_cavity2(mesh, points, sub_determinants, CAVITY, current_tet, i);
79
+ // tetrahedral_mesh_compute_cavity(mesh, CAVITY, points, current_tet, i);
80
+
81
+ current_tet = fill_in_a_cavity(mesh, points, sub_determinants, CAVITY, current_tet);
82
+
49
83
  }
50
84
 
51
85
  // remove tetras formed by with the super bounding volume
52
- tetrahedrons.removeTetrasConnectedToPoints(points, n, n + 3);
86
+ mesh.removeTetrasConnectedToPoints(points, n, n + 3);
53
87
 
54
88
 
55
- return tetrahedrons;
56
- }
89
+ debug_validate_mesh(mesh, points);
57
90
 
91
+ return mesh;
92
+ }
58
93
 
@@ -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
+ }
@@ -0,0 +1,155 @@
1
+ import { assert } from "../../../../assert.js";
2
+ import { tetrahedral_mesh_compute_sub_determinant } from "./tetrahedral_mesh_compute_sub_determinant.js";
3
+ import { array_copy } from "../../../../collection/array/copyArray.js";
4
+ import { INVALID_NEIGHBOUR } from "../TetrahedralMesh.js";
5
+ import { debug_validate_mesh } from "./debug_validate_mesh.js";
6
+
7
+ /**
8
+ *
9
+ * @param {TetrahedralMesh} mesh
10
+ * @param {ArrayLike<number>|number[]|Float32Array} points
11
+ * @param {ArrayLike<number>|number[]|Float32Array} subdets
12
+ * @param {Cavity} cavity
13
+ * @param {number} cur_tet
14
+ * @returns {number} new current tet
15
+ */
16
+ export function fill_in_a_cavity(mesh, points, subdets, cavity, cur_tet) {
17
+
18
+ let clength = cavity.__deleted_size; // cavity size
19
+ const blength = cavity.__boundary_size; // boundary size
20
+
21
+ assert.equal(blength & 1, 0, 'there is always a pair number of triangle on the boundary. N points, 2N triangle, 3N edges');
22
+
23
+ /*
24
+ Re-use tets that would be deleted, if there are not enough dead tets - allocate new ones as necessary
25
+ */
26
+ if (blength > clength) {
27
+
28
+ for (let i = clength; i < blength; i++) {
29
+ // allocate tets for the cavity filling, remember them inside the "deleted" set for now
30
+ cavity.__deleted[i] = mesh.allocate();
31
+ }
32
+
33
+ clength = blength;
34
+ }
35
+
36
+ const boundary = cavity.__boundary;
37
+ const start = clength - blength;
38
+
39
+ for (let i = 0; i < blength; i++) {
40
+ // go over the faces in the cavity boundary and matching tet that will be filling the space
41
+
42
+ const current_tet_index = cavity.__deleted[i + start];
43
+
44
+ const i5 = i * 5;
45
+
46
+ // write filling tet's corners
47
+ mesh.setVertexIndex(current_tet_index, 0, boundary[i5]); // "ball"/cavity center point is written here
48
+ mesh.setVertexIndex(current_tet_index, 1, boundary[i5 + 1]);
49
+ mesh.setVertexIndex(current_tet_index, 2, boundary[i5 + 2]);
50
+ mesh.setVertexIndex(current_tet_index, 3, boundary[i5 + 3]);
51
+
52
+ const encoded_boundary_tet = boundary[i5 + 4];
53
+
54
+ mesh.setNeighbour(current_tet_index, 0, encoded_boundary_tet); // connect opposing neighbour
55
+
56
+ const encoded_current_tet = current_tet_index << 2;
57
+
58
+ if (encoded_boundary_tet !== INVALID_NEIGHBOUR) {
59
+ // update neighbour
60
+ mesh.setNeighbour(encoded_boundary_tet >> 2, encoded_boundary_tet & 3, encoded_current_tet);
61
+ }
62
+
63
+ // boundary[i5 + 4] = encoded_current_tet;
64
+
65
+ // compute SubDet
66
+ tetrahedral_mesh_compute_sub_determinant(subdets, current_tet_index * 4, mesh, points, current_tet_index);
67
+ }
68
+
69
+ computeAdjacencies(mesh, cavity, start, blength);
70
+ // computeAdjacenciesSlow(mesh, cavity, start, blength);
71
+
72
+ debug_validate_mesh(mesh, points);
73
+
74
+ cavity.__boundary_size = 0;
75
+ cavity.__deleted_size = start;
76
+
77
+
78
+ return cavity.__deleted[start];
79
+
80
+ }
81
+
82
+ /**
83
+ * filling back the cavity
84
+ * N+2 point on the surface of the cavity
85
+ * 2N triangle on the surface of the cavity, x3 (4*0.5+1) data = 6N+9 uint64_t
86
+ * => enough place for the 3N edge x2 data = 6N uint64_t
87
+ * @param {TetrahedralMesh} mesh
88
+ * @param {Cavity} cavity
89
+ * @param {number} start
90
+ * @param {number} blength
91
+ */
92
+ function computeAdjacencies(mesh, cavity, start, blength) {
93
+ const boundary = cavity.__boundary;
94
+
95
+ let tlength = 0;
96
+
97
+ const Tmp = boundary; // we know there is enough space...
98
+ const index = [2, 3, 1, 2];
99
+
100
+ for (let i = 0; i < blength; i++) {
101
+
102
+ const cur_tet = cavity.__deleted[start + i];
103
+
104
+ // pointer to the position of Node[0] in the Tmp array
105
+
106
+ for (let j = 0; j < 3; j++) {
107
+ // define the edge by the minimum vertex and the other
108
+ const n0 = mesh.getVertexIndex(cur_tet, index[j]);
109
+ const n1 = mesh.getVertexIndex(cur_tet, index[j + 1]);
110
+
111
+ const current_tet_neighbour_id = j + 1;
112
+ const encoded_current_tet = (cur_tet << 2) | (current_tet_neighbour_id & 3);
113
+
114
+ // linear searching/pushing into Tmp
115
+ let k = 0;
116
+ for (; k < tlength; k++) {
117
+ // this is the only nested loop... the one that cost it all
118
+
119
+ if (Tmp[k * 3] === n0 && Tmp[k * 3 + 1] === n1)
120
+ // found a match, stop here
121
+ break;
122
+ }
123
+
124
+ if (k === tlength) {
125
+ // we did not find it
126
+ const offset = tlength * 3;
127
+
128
+ // flip the edge order and store it. Neighbouring tet will have edge order reversed
129
+ Tmp[offset] = n1;
130
+ Tmp[offset + 1] = n0;
131
+
132
+ Tmp[offset + 2] = encoded_current_tet;
133
+
134
+ tlength++;
135
+ } else {
136
+ // we found the neighbour !
137
+ const encoded_pairValue = Tmp[k * 3 + 2];
138
+
139
+ mesh.setNeighbour(cur_tet, current_tet_neighbour_id, encoded_pairValue);
140
+ mesh.setNeighbour(encoded_pairValue >> 2, encoded_pairValue & 3, encoded_current_tet);
141
+
142
+ // reduce search space, there can only be one neighbour pair, after it's found we can remove the entry from lookup table
143
+ tlength--;
144
+
145
+ if (k < tlength) {
146
+ // put the last entry in the one we just discovered
147
+ array_copy(Tmp, tlength * 3, Tmp, k * 3, 3);
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ assert.equal(tlength, 0, 'Failed to compute adjacencies');
154
+
155
+ }
@@ -0,0 +1,224 @@
1
+ import { assert } from "../../../../assert.js";
2
+ import { point_in_tetrahedron_circumsphere } from "../point_in_tetrahedron_circumsphere.js";
3
+ import { INVALID_NEIGHBOUR } from "../TetrahedralMesh.js";
4
+
5
+
6
+ /**
7
+ *
8
+ * @param {Cavity} cavity
9
+ * @param {TetrahedralMesh} mesh
10
+ * @param {number} tet
11
+ * @param {number} point_index
12
+ * @param {number} k0
13
+ * @param {number} k1
14
+ * @param {number} k2
15
+ * @param {number} k3
16
+ */
17
+ function store_special(cavity, mesh, tet, point_index, k0, k1, k2, k3) {
18
+
19
+ const node_0 = mesh.getVertexIndex(tet, k0);
20
+ const node_1 = mesh.getVertexIndex(tet, k1);
21
+ const node_2 = mesh.getVertexIndex(tet, k2);
22
+
23
+ cavity.push_boundary(
24
+ point_index,
25
+ node_0,
26
+ node_1,
27
+ node_2,
28
+ mesh.getNeighbour(tet, k3),
29
+ );
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param {TetrahedralMesh} mesh
35
+ * @param {Cavity} cavity
36
+ * @param {number[]|Float32Array} points
37
+ * @param {Float32Array} subdets
38
+ * @param {number} containing_tetra tetrahedron that contains point
39
+ * @param {number} point_index point that forms the cavity
40
+ */
41
+ export function tetrahedral_mesh_compute_cavity3(
42
+ mesh,
43
+ points,
44
+ subdets,
45
+ cavity,
46
+ containing_tetra,
47
+ point_index
48
+ ) {
49
+ assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');
50
+
51
+ // add the tet to cavity
52
+ cavity.push_deleted(containing_tetra);
53
+ subdets[containing_tetra * 4 + 3] = -1;
54
+
55
+ for (let start = cavity.__deleted_size - 1; start < cavity.__deleted_size; start++) {
56
+ const cur_tet = cavity.__deleted[start];
57
+
58
+ for (let i = 0; i < 4; i++) {
59
+ const encoded_neighbour = mesh.getNeighbour(cur_tet, i);
60
+
61
+ const k0 = (1 << i) & 3;
62
+ const k1 = (i + 2) % 3;
63
+ const k2 = (~((i + 1) >> 1) & 2) + 1;
64
+ const k3 = i;
65
+
66
+ if (encoded_neighbour === INVALID_NEIGHBOUR) {
67
+ // no neighbour, include this side into boundary
68
+
69
+ const node_0 = mesh.getVertexIndex(cur_tet, k0);
70
+ const node_1 = mesh.getVertexIndex(cur_tet, k1);
71
+ const node_2 = mesh.getVertexIndex(cur_tet, k2);
72
+
73
+ if(i === 3 || node_2 >= node_1) {
74
+ cavity.push_boundary(
75
+ point_index,
76
+ node_2,
77
+ node_0,
78
+ node_1,
79
+ mesh.getNeighbour(cur_tet, k3),
80
+ );
81
+ }else{
82
+ cavity.push_boundary(
83
+ point_index,
84
+ node_0,
85
+ node_1,
86
+ node_2,
87
+ mesh.getNeighbour(cur_tet, k3),
88
+ );
89
+ }
90
+
91
+ continue;
92
+ }
93
+
94
+ const neighbour_index = encoded_neighbour >> 2;
95
+
96
+ if (subdets[neighbour_index * 4 + 3] === -1) {
97
+ // already visited
98
+ continue;
99
+ }
100
+
101
+ if (point_in_tetrahedron_circumsphere(
102
+ points,
103
+ mesh.getVertexIndex(neighbour_index, 0),
104
+ mesh.getVertexIndex(neighbour_index, 1),
105
+ mesh.getVertexIndex(neighbour_index, 2),
106
+ mesh.getVertexIndex(neighbour_index, 3),
107
+ point_index
108
+ ) < 0) {
109
+ // outside the cavity, meaning it defines boundary
110
+
111
+ const node_0 = mesh.getVertexIndex(cur_tet, k0);
112
+ const node_1 = mesh.getVertexIndex(cur_tet, k1);
113
+ const node_2 = mesh.getVertexIndex(cur_tet, k2);
114
+
115
+ if(i === 3 || node_2 >= node_1) {
116
+ cavity.push_boundary(
117
+ point_index,
118
+ node_2,
119
+ node_0,
120
+ node_1,
121
+ mesh.getNeighbour(cur_tet, k3),
122
+ );
123
+ }else{
124
+ cavity.push_boundary(
125
+ point_index,
126
+ node_0,
127
+ node_1,
128
+ node_2,
129
+ mesh.getNeighbour(cur_tet, k3),
130
+ );
131
+ }
132
+ } else {
133
+ //part of the cavity, remove
134
+ cavity.push_deleted(neighbour_index);
135
+ subdets[neighbour_index * 4 + 3] = -1;
136
+ }
137
+ }
138
+
139
+ }
140
+ }
141
+
142
+
143
+ /**
144
+ *
145
+ * @param {TetrahedralMesh} mesh
146
+ * @param {Cavity} cavity
147
+ * @param {number[]|Float32Array} points
148
+ * @param {Float32Array} subdets
149
+ * @param {number} containing_tetra tetrahedron that contains point
150
+ * @param {number} point_index point that forms the cavity
151
+ */
152
+ export function tetrahedral_mesh_compute_cavity2(
153
+ mesh,
154
+ points,
155
+ subdets,
156
+ cavity,
157
+ containing_tetra,
158
+ point_index
159
+ ) {
160
+ assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');
161
+
162
+ // add the tet to cavity
163
+ cavity.push_deleted(containing_tetra);
164
+ subdets[containing_tetra * 4 + 3] = -1;
165
+
166
+ for (let start = cavity.__deleted_size - 1; start < cavity.__deleted_size; start++) {
167
+ const cur_tet = cavity.__deleted[start];
168
+
169
+ for (let i = 0; i < 4; i++) {
170
+ const encoded_neighbour = mesh.getNeighbour(cur_tet, i);
171
+
172
+ const k0 = (1 << i) & 3;
173
+ const k1 = (i + 2) % 3;
174
+ const k2 = (~((i + 1) >> 1) & 2) + 1;
175
+ const k3 = i;
176
+
177
+ if (encoded_neighbour === INVALID_NEIGHBOUR) {
178
+ // no neighbour, include this side into boundary
179
+
180
+ cavity.push_boundary(
181
+ point_index,
182
+ mesh.getVertexIndex(cur_tet, k0),
183
+ mesh.getVertexIndex(cur_tet, k1),
184
+ mesh.getVertexIndex(cur_tet, k2),
185
+ mesh.getNeighbour(cur_tet, k3),
186
+ );
187
+
188
+ continue;
189
+ }
190
+
191
+ const neighbour_index = encoded_neighbour >> 2;
192
+
193
+ if (subdets[neighbour_index * 4 + 3] === -1) {
194
+ // already visited
195
+ continue;
196
+ }
197
+
198
+ if (point_in_tetrahedron_circumsphere(
199
+ points,
200
+ mesh.getVertexIndex(neighbour_index, 0),
201
+ mesh.getVertexIndex(neighbour_index, 1),
202
+ mesh.getVertexIndex(neighbour_index, 2),
203
+ mesh.getVertexIndex(neighbour_index, 3),
204
+ point_index
205
+ ) < 0) {
206
+ // outside the cavity, meaning it defines boundary
207
+
208
+ cavity.push_boundary(
209
+ point_index,
210
+ mesh.getVertexIndex(cur_tet, k0),
211
+ mesh.getVertexIndex(cur_tet, k1),
212
+ mesh.getVertexIndex(cur_tet, k2),
213
+ mesh.getNeighbour(cur_tet, k3),
214
+ );
215
+
216
+ } else {
217
+ //part of the cavity, remove
218
+ cavity.push_deleted(neighbour_index);
219
+ subdets[neighbour_index * 4 + 3] = -1;
220
+ }
221
+ }
222
+
223
+ }
224
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Compute sub-determinants for a given tetrahedron
3
+ * NOTE: this implementation doesn't use GHOST vertices, all tets/vertices are assumed to be real and valid
4
+ * @see https://git.immc.ucl.ac.be/hextreme/hxt_seqdel/-/blob/master/src/hxt_tetrahedra.c#L754
5
+ * @param {number[]|Float32Array} result
6
+ * @param {number} result_offset
7
+ * @param {TetrahedralMesh} mesh
8
+ * @param {number[]|Float32Array} points
9
+ * @param {number} tetrahedron_index
10
+ */
11
+ export function tetrahedral_mesh_compute_sub_determinant(result, result_offset, mesh, points, tetrahedron_index) {
12
+ /*
13
+ NOTE: the code is re-ordered slightly and inlined for speed, re-ordering is mostly to reduce register pressure
14
+ */
15
+ const a_index = mesh.getVertexIndex(tetrahedron_index, 0);
16
+ const b_index = mesh.getVertexIndex(tetrahedron_index, 1);
17
+ const c_index = mesh.getVertexIndex(tetrahedron_index, 2);
18
+ const d_index = mesh.getVertexIndex(tetrahedron_index, 3);
19
+
20
+ // read out coordinates
21
+ const a3 = a_index * 3;
22
+ const b3 = b_index * 3;
23
+ const c3 = c_index * 3;
24
+ const d3 = d_index * 3;
25
+
26
+ const a_x = points[a3];
27
+ const a_y = points[a3 + 1];
28
+ const a_z = points[a3 + 2];
29
+
30
+ const b_x = points[b3];
31
+ const b_y = points[b3 + 1];
32
+ const b_z = points[b3 + 2];
33
+
34
+ // construct AB, AC, AD
35
+ const ab_x = b_x - a_x;
36
+ const ab_y = b_y - a_y;
37
+ const ab_z = b_z - a_z;
38
+
39
+ const c_x = points[c3];
40
+ const c_y = points[c3 + 1];
41
+ const c_z = points[c3 + 2];
42
+
43
+ const ac_x = c_x - a_x;
44
+ const ac_y = c_y - a_y;
45
+ const ac_z = c_z - a_z;
46
+
47
+ const d_x = points[d3];
48
+ const d_y = points[d3 + 1];
49
+ const d_z = points[d3 + 2];
50
+
51
+ const ad_x = d_x - a_x;
52
+ const ad_y = d_y - a_y;
53
+ const ad_z = d_z - a_z;
54
+
55
+ const ab_w = ab_x * ab_x + ab_y * ab_y + ab_z * ab_z;
56
+ const ac_w = ac_x * ac_x + ac_y * ac_y + ac_z * ac_z;
57
+ const ad_w = ad_x * ad_x + ad_y * ad_y + ad_z * ad_z;
58
+
59
+ const cd12 = ac_z * ad_y - ac_y * ad_z;
60
+ const db12 = ad_z * ab_y - ad_y * ab_z;
61
+ const bc12 = ab_z * ac_y - ab_y * ac_z;
62
+
63
+ const cd30 = ac_x * ad_w - ac_w * ad_x;
64
+ const db30 = ad_x * ab_w - ad_w * ab_x;
65
+ const bc30 = ab_x * ac_w - ab_w * ac_x;
66
+
67
+ // each subdet is a simple triple product
68
+ const subdet_0 = ab_w * cd12 + ac_w * db12 + ad_w * bc12;
69
+ const subdet_1 = ab_z * cd30 + ac_z * db30 + ad_z * bc30;
70
+ const subdet_2 = ab_y * cd30 + ac_y * db30 + ad_y * bc30;
71
+ const subdet_3 = ab_x * cd12 + ac_x * db12 + ad_x * bc12;
72
+
73
+ result[result_offset] = subdet_0;
74
+ result[result_offset + 1] = subdet_1;
75
+ result[result_offset + 2] = subdet_2;
76
+ result[result_offset + 3] = subdet_3;
77
+ }