@woosh/meep-engine 2.43.1 → 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.
- 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/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/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 +8 -10
- 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/tetrahedra/README.md +10 -1
- package/core/geom/3d/tetrahedra/{tetrahedra_collection.js → TetrahedralMesh.js} +236 -152
- package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +156 -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 +45 -7
- package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +44 -9
- package/core/geom/3d/tetrahedra/delaunay/debug_validate_mesh.js +19 -0
- package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +155 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity2.js +224 -0
- 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_insert_point.js +98 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +13 -6
- package/core/geom/3d/tetrahedra/point_in_tetrahedron_circumsphere.js +9 -9
- package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +1 -1
- 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 +119 -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/task/util/randomCountTask.js +1 -1
- 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/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/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/geometry/VertexDataSpec.js +1 -1
- 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/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 +7 -1
- 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/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 +1 -1
- 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_compute_cavity.js +0 -73
- 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
|
@@ -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', () => {
|
|
@@ -4,13 +4,50 @@ export class Cavity {
|
|
|
4
4
|
*
|
|
5
5
|
* @type {number[]}
|
|
6
6
|
*/
|
|
7
|
-
this.
|
|
7
|
+
this.__deleted = [];
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
*
|
|
11
11
|
* @type {number}
|
|
12
12
|
*/
|
|
13
|
-
this.
|
|
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
|
-
|
|
25
|
-
this.
|
|
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.
|
|
71
|
+
const n = this.__deleted_size;
|
|
35
72
|
|
|
36
73
|
for (let j = 0; j < n; j++) {
|
|
37
|
-
if (this.
|
|
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.
|
|
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 "../
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
mesh.removeTetrasConnectedToPoints(points, n, n + 3);
|
|
53
87
|
|
|
54
88
|
|
|
55
|
-
|
|
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
|
+
}
|