@woosh/meep-engine 2.33.35 → 2.33.39

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 (67) hide show
  1. package/core/collection/HashMap.js +19 -12
  2. package/core/collection/table/RowFirstTable.js +13 -8
  3. package/core/collection/table/RowFirstTableSpec.js +6 -0
  4. package/core/color/Color.js +5 -9
  5. package/core/color/ColorUtils.js +4 -150
  6. package/core/color/YCbCr_to_rgb_uint24.js +20 -0
  7. package/core/color/hsv2rgb.js +54 -0
  8. package/core/color/int2rgb.js +12 -0
  9. package/core/color/parseHex.js +18 -0
  10. package/core/color/rgb2hex.js +16 -0
  11. package/core/color/rgb2hsv.js +47 -0
  12. package/core/color/rgb2uint24.js +17 -0
  13. package/core/color/rgb_to_YCbCr_uint24.js +18 -0
  14. package/core/geom/3d/aabb/compute_aabb_from_points.js +28 -0
  15. package/core/geom/3d/plane/orient3d_fast.js +51 -0
  16. package/core/geom/3d/plane/plane_computeConvex3PlaneIntersection.js +5 -0
  17. package/core/geom/3d/tetrahedra/GetDepthForTetrahedronProbe.glsl +43 -0
  18. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +177 -0
  19. package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.spec.js +66 -0
  20. package/core/geom/3d/tetrahedra/compute_circumsphere.js +271 -0
  21. package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.js +50 -0
  22. package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.spec.js +14 -0
  23. package/core/geom/3d/tetrahedra/point_in_tetrahedron_circumsphere.js +77 -0
  24. package/core/geom/3d/tetrahedra/tetrahedra_collection.js +279 -0
  25. package/core/geom/3d/tetrahedra/tetrahedron_contains_point.js +25 -0
  26. package/core/geom/Quaternion.js +4 -0
  27. package/core/math/hash/murmur3_32.js +39 -0
  28. package/core/process/undo/ActionProcessor.d.ts +2 -0
  29. package/core/process/undo/ActionProcessor.js +15 -4
  30. package/editor/tools/FoliagePaintTool.js +1 -1
  31. package/editor/tools/GridPaintTool.js +1 -1
  32. package/engine/asset/loaders/GLTFAssetLoader.js +4 -8
  33. package/engine/ecs/foliage/InstancedFoliage.js +45 -0
  34. package/engine/ecs/foliage/ecs/InstancedMeshLayer.js +4 -0
  35. package/engine/ecs/storage/BinaryBufferSerializer.js +48 -38
  36. package/engine/ecs/storage/binary/collection/BinaryCollectionSerializer.js +1 -1
  37. package/engine/ecs/terrain/ecs/splat/SplatMapOptimizerDebugger.js +1 -1
  38. package/engine/ecs/terrain/tiles/TerrainTile.js +1 -1
  39. package/engine/graphics/GraphicsEngine.js +7 -0
  40. package/engine/graphics/ecs/mesh/Mesh.js +2 -2
  41. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +13 -0
  42. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +339 -314
  43. package/engine/graphics/micron/build/PatchRepresentation.js +1 -1
  44. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -1
  45. package/engine/graphics/micron/build/fill_patch_geometry_data.js +1 -1
  46. package/engine/graphics/micron/format/serialization/deserialize_patch.js +1 -1
  47. package/engine/graphics/micron/format/serialization/serialize_patch.js +1 -1
  48. package/engine/graphics/micron/prototypeVirtualGeometry.js +4 -1
  49. package/engine/graphics/micron/render/instanced/PatchDataTextures.js +3 -3
  50. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -1
  51. package/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +1 -1
  52. package/engine/graphics/micron/render/makeThreeMeshFromVirtualGeometry.js +1 -1
  53. package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +2 -2
  54. package/engine/graphics/micron/render/v1/ThreeVirtualGeometryAdapter.js +1 -1
  55. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +7 -3
  56. package/engine/graphics/particles/node-based/rendering/billboard/ParticleBillboardShader.js +1 -1
  57. package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +100 -55
  58. package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +6 -1
  59. package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +1 -0
  60. package/engine/graphics/render/forward_plus/cluster/compute_light_data_hash_0.js +1 -1
  61. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +17 -4
  62. package/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.js +1 -1
  63. package/engine/graphics/render/visibility/VisibilityComputer.js +8 -7
  64. package/engine/graphics/shaders/TerrainShader.js +5 -5
  65. package/engine/graphics/shadows/testShadowMapRendering.js +192 -0
  66. package/package.json +3 -1
  67. package/view/elements/ColorPickerView.js +2 -1
@@ -0,0 +1,177 @@
1
+ import { v3_dot } from "../../v3_dot.js";
2
+ import { min2 } from "../../../math/min2.js";
3
+ import { max2 } from "../../../math/max2.js";
4
+ import { plane_computeConvex3PlaneIntersection } from "../plane/plane_computeConvex3PlaneIntersection.js";
5
+
6
+ /**
7
+ * Planes used to bound the simplex
8
+ * NOTE: these might be computed for a given set of points instead, to produce a tighter fit, but this was good enough for my needs so far
9
+ *
10
+ * @readonly
11
+ * @type {Float32Array}
12
+ */
13
+ const SIMPLEX_PROJECTION_AXES = new Float32Array([
14
+ Math.sqrt(8 / 9), 0, -1 / 3.,
15
+ -Math.sqrt(2 / 9), Math.sqrt(2 / 3), -1 / 3,
16
+ -Math.sqrt(2 / 9), -Math.sqrt(2 / 3), -1 / 3,
17
+ 0, 0, 1
18
+ ]);
19
+
20
+ /**
21
+ * Compute bounding tetrahedron (3d simplex) for a given set of input points
22
+ *
23
+ * @see https://github.com/bryanmcnett/aabo/blob/master/aabo.cpp
24
+ * @author Alexander Goldring (c) 2021
25
+ *
26
+ * @param {number[]|Float32Array|Float64Array} result
27
+ * @param {number} result_offset
28
+ * @param {number[]|Float32Array|Float64Array} input
29
+ * @param {number} input_count
30
+ * @param {number} padding Planar offset for each of the simple sides, positive padding value will create a larger simplex. Useful for ensuring that input points do not lie directly on the simplex planes
31
+ */
32
+ export function compute_bounding_simplex_3d(
33
+ result,
34
+ result_offset,
35
+ input,
36
+ input_count,
37
+ padding
38
+ ) {
39
+ if (input_count <= 0) {
40
+ // no inputs
41
+ return;
42
+ }
43
+
44
+ // initialize
45
+ let x = input[0];
46
+ let y = input[1];
47
+ let z = input[2];
48
+
49
+ let a = v3_dot(
50
+ x,
51
+ y,
52
+ z,
53
+ SIMPLEX_PROJECTION_AXES[0],
54
+ SIMPLEX_PROJECTION_AXES[1],
55
+ SIMPLEX_PROJECTION_AXES[2],
56
+ );
57
+ let b = v3_dot(
58
+ x,
59
+ y,
60
+ z,
61
+ SIMPLEX_PROJECTION_AXES[3],
62
+ SIMPLEX_PROJECTION_AXES[4],
63
+ SIMPLEX_PROJECTION_AXES[5],
64
+ );
65
+ let c = v3_dot(
66
+ x,
67
+ y,
68
+ z,
69
+ SIMPLEX_PROJECTION_AXES[6],
70
+ SIMPLEX_PROJECTION_AXES[7],
71
+ SIMPLEX_PROJECTION_AXES[8],
72
+ );
73
+ let d = v3_dot(
74
+ x,
75
+ y,
76
+ z,
77
+ SIMPLEX_PROJECTION_AXES[9],
78
+ SIMPLEX_PROJECTION_AXES[10],
79
+ SIMPLEX_PROJECTION_AXES[11],
80
+ );
81
+
82
+ let min_a = a;
83
+ let min_b = b;
84
+ let min_c = c;
85
+ let min_d = d;
86
+
87
+ let max_a = a;
88
+ let max_b = b;
89
+ let max_c = c;
90
+ let max_d = d;
91
+
92
+ for (let i = 1; i < input_count; i++) {
93
+
94
+ const i3 = i * 3;
95
+
96
+ x = input[i3];
97
+ y = input[i3 + 1];
98
+ z = input[i3 + 2];
99
+
100
+ a = v3_dot(
101
+ x,
102
+ y,
103
+ z,
104
+ SIMPLEX_PROJECTION_AXES[0],
105
+ SIMPLEX_PROJECTION_AXES[1],
106
+ SIMPLEX_PROJECTION_AXES[2],
107
+ );
108
+ b = v3_dot(
109
+ x,
110
+ y,
111
+ z,
112
+ SIMPLEX_PROJECTION_AXES[3],
113
+ SIMPLEX_PROJECTION_AXES[4],
114
+ SIMPLEX_PROJECTION_AXES[5],
115
+ );
116
+ c = v3_dot(
117
+ x,
118
+ y,
119
+ z,
120
+ SIMPLEX_PROJECTION_AXES[6],
121
+ SIMPLEX_PROJECTION_AXES[7],
122
+ SIMPLEX_PROJECTION_AXES[8],
123
+ );
124
+ d = v3_dot(
125
+ x,
126
+ y,
127
+ z,
128
+ SIMPLEX_PROJECTION_AXES[9],
129
+ SIMPLEX_PROJECTION_AXES[10],
130
+ SIMPLEX_PROJECTION_AXES[11],
131
+ );
132
+
133
+ min_a = min2(min_a, a);
134
+ min_b = min2(min_b, b);
135
+ min_c = min2(min_c, c);
136
+ min_d = min2(min_d, d);
137
+
138
+ max_a = max2(max_a, a);
139
+ max_b = max2(max_b, b);
140
+ max_c = max2(max_c, c);
141
+ max_d = max2(max_d, d);
142
+ }
143
+
144
+ // we now have the planar offset for each projection axis
145
+
146
+ // apply padding offset
147
+ min_a -= padding;
148
+ min_b -= padding;
149
+ min_c -= padding;
150
+ min_d -= padding;
151
+
152
+ // lets compute intersection points for each tri-plane combination to find simplex corners
153
+ plane_computeConvex3PlaneIntersection(
154
+ result, result_offset,
155
+ SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
156
+ SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
157
+ SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
158
+ );
159
+ plane_computeConvex3PlaneIntersection(
160
+ result, result_offset + 3,
161
+ SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
162
+ SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
163
+ SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
164
+ );
165
+ plane_computeConvex3PlaneIntersection(
166
+ result, result_offset + 6,
167
+ SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
168
+ SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
169
+ SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
170
+ );
171
+ plane_computeConvex3PlaneIntersection(
172
+ result, result_offset + 9,
173
+ SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
174
+ SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
175
+ SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
176
+ );
177
+ }
@@ -0,0 +1,66 @@
1
+ import { compute_bounding_simplex_3d } from "./compute_bounding_simplex_3d.js";
2
+ import { v3_distance } from "../../v3_distance.js";
3
+ import { orient3d_fast } from "../plane/orient3d_fast.js";
4
+
5
+ test('one point', () => {
6
+ const r = [];
7
+ compute_bounding_simplex_3d(r, 0, [1, 2, 3], 1, 0);
8
+
9
+ for (let i = 0; i < 4; i++) {
10
+ const i3 = i * 3;
11
+
12
+ expect(r[i3]).toBeCloseTo(1);
13
+ expect(r[i3 + 1]).toBeCloseTo(2);
14
+ expect(r[i3 + 2]).toBeCloseTo(3);
15
+ }
16
+ });
17
+
18
+ test('planar containment, one point', () => {
19
+ const r = [];
20
+
21
+ compute_bounding_simplex_3d(r, 0, [0, 0, 0], 1, 1);
22
+
23
+ r.push(0, 0, 0);
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);
29
+ });
30
+
31
+ test('positive padding results in larger simplex', () => {
32
+ const r_0 = [];
33
+ const r_n = [];
34
+ const r_p = [];
35
+
36
+ const volume = [
37
+ -100, 0, 0,
38
+ 100, 0, 0,
39
+ 0, -100, 0,
40
+ 0, 100, 0,
41
+ 0, 0, -100,
42
+ 0, 0, 100,
43
+ ];
44
+
45
+ compute_bounding_simplex_3d(r_0, 0, volume, 6, 0);
46
+ compute_bounding_simplex_3d(r_n, 0, volume, 6, -1);
47
+ compute_bounding_simplex_3d(r_p, 0, volume, 6, 1);
48
+
49
+ function compute_distance_sum(ref, points, point_count) {
50
+ let r = 0;
51
+ for (let i = 0; i < point_count; i++) {
52
+ const i3 = i * 3;
53
+
54
+ r += v3_distance(points[i3], points[i3 + 1], points[i3 + 2], ref[0], ref[1], ref[2]);
55
+ }
56
+
57
+ return r;
58
+ }
59
+
60
+ const d_0 = compute_distance_sum([0, 0, 0], r_0, 4);
61
+ const d_n = compute_distance_sum([0, 0, 0], r_n, 4);
62
+ const d_p = compute_distance_sum([0, 0, 0], r_p, 4);
63
+
64
+ expect(d_0).toBeLessThan(d_p);
65
+ expect(d_0).toBeGreaterThan(d_n);
66
+ });
@@ -0,0 +1,271 @@
1
+ import { array_copy } from "../../../collection/array/copyArray.js";
2
+ import { v3_dot } from "../../v3_dot.js";
3
+
4
+ const MAX_FLOAT32 = 3.4028234663852886e+38;
5
+ const SMALL_NUMBER = 1e-12;
6
+ const VTK_MAX_WARNS = 3;
7
+
8
+ /**
9
+ * Compute the circumcenter (center[3]) and radius squared (method
10
+ * return value) of a tetrahedron defined by the four points x1, x2,
11
+ * x3, and x4.
12
+ *
13
+ * @see https://github.com/Kitware/VTK/blob/ac3fd8005bce7b3da4423c305f61ffd9df9695ef/Common/DataModel/vtkTetra.cxx
14
+ * @param {number[]} result [center_x, center_y, center_z, radius_squared]
15
+ * @param {number[]} points
16
+ * @param {number} a
17
+ * @param {number} b
18
+ * @param {number} c
19
+ * @param {number} d
20
+ */
21
+ export function compute_circumsphere(
22
+ result,
23
+ points,
24
+ a, b, c, d
25
+ ) {
26
+ const n12 = [];
27
+ const n13 = [];
28
+ const n14 = [];
29
+
30
+ const x12 = [];
31
+ const x13 = [];
32
+ const x14 = [];
33
+
34
+ // calculate normals and intersection points of bisecting planes.
35
+ for (let i = 0; i < 3; i++) {
36
+ const v_a = points[a * 3 + i];
37
+
38
+ const v_b = points[b * 3 + i];
39
+ n12[i] = v_b - v_a;
40
+ const v_c = points[c * 3 + i];
41
+ n13[i] = v_c - v_a;
42
+ const v_d = points[d * 3 + i];
43
+ n14[i] = v_d - v_a;
44
+
45
+ x12[i] = (v_b + v_a) * 0.5;
46
+ x13[i] = (v_c + v_a) * 0.5;
47
+ x14[i] = (v_d + v_a) * 0.5;
48
+ }
49
+
50
+ const A = [];
51
+
52
+ array_copy(n12, 0, A, 0, 3);
53
+ array_copy(n13, 0, A, 3, 3);
54
+ array_copy(n14, 0, A, 6, 3);
55
+
56
+ const rhs = [];
57
+
58
+ rhs[0] = v3_dot(n12[0], n12[1], n12[2], x12[0], x12[1], x12[2]);
59
+ rhs[1] = v3_dot(n13[0], n13[1], n13[2], x13[0], x13[1], x13[2]);
60
+ rhs[2] = v3_dot(n14[0], n14[1], n14[2], x14[0], x14[1], x14[2]);
61
+
62
+ // Solve system of equations
63
+ if (solve_linear_system(A, rhs, 3) === false) {
64
+ // failed to solve, fall-back to infinitely-sized sphere centered around origin
65
+ result[0] = result[1] = result[2] = 0;
66
+ result[3] = MAX_FLOAT32;
67
+ return;
68
+ } else {
69
+ result[0] = rhs[0];
70
+ result[1] = rhs[1];
71
+ result[2] = rhs[2];
72
+ }
73
+
74
+ let sum = 0;
75
+ let diff;
76
+
77
+ for (let i = 0; i < 3; i++) {
78
+ const rhs_offset = rhs[i];
79
+
80
+ diff = points[a * 3 + i] - rhs_offset;
81
+ sum += diff * diff;
82
+ diff = points[b * 3 + i] - rhs_offset;
83
+ sum += diff * diff;
84
+ diff = points[c * 3 + i] - rhs_offset;
85
+ sum += diff * diff;
86
+ diff = points[d * 3 + i] - rhs_offset;
87
+ sum += diff * diff;
88
+ }
89
+
90
+ // divide
91
+ sum *= 0.25;
92
+
93
+ if (sum >= MAX_FLOAT32) {
94
+ result[3] = MAX_FLOAT32;
95
+ } else {
96
+ result[3] = sum;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Solve linear equations Ax = b using Crout's method. Input is square matrix A
102
+ * and load vector b. Solution x is written over load vector. The dimension of
103
+ * the matrix is specified in size. If error is found, method returns a 0.
104
+ *
105
+ * @see https://github.com/Kitware/VTK/blob/af928016f4041ad8a6a193d032355b909721fc95/Common/Core/vtkMath.cxx
106
+ *
107
+ * @param {number[]} A
108
+ * @param {number[]} x
109
+ * @param {number} size
110
+ * @returns {boolean}
111
+ */
112
+ function solve_linear_system(A, x, size) {
113
+ const index = [];
114
+
115
+ if (luf_factor_linear_system(A, index, size) === 0) {
116
+ return false;
117
+ }
118
+
119
+ luf_solve_linear_system(A, index, x, size);
120
+
121
+ return true;
122
+ }
123
+
124
+
125
+ /**
126
+ * Factor linear equations Ax = b using LU decomposition A = LU where L is
127
+ * lower triangular matrix and U is upper triangular matrix. Input is
128
+ * square matrix A, integer array of pivot indices index[0->n-1], and size
129
+ * of square matrix n. Output factorization LU is in matrix A. If error is
130
+ * found, method returns 0.
131
+ * @param {number[]} A
132
+ * @param {number[]} index
133
+ * @param {number} size
134
+ */
135
+ function luf_factor_linear_system(A, index, size) {
136
+ const scale = [];
137
+
138
+ // double scratch[10];
139
+ // double* scale = (size < 10 ? scratch : new double[size]);
140
+ //
141
+ // int i, j, k;
142
+ // int maxI = 0;
143
+ // double largest, temp1, temp2, sum;
144
+ //
145
+ // // Manage number of output warnings
146
+ // static int numWarns = 0;
147
+ //
148
+ // //
149
+
150
+ let numWarns = 0;
151
+ let i, j, k;
152
+ let maxI = 0;
153
+ let largest, temp1, temp2, sum;
154
+ // Loop over rows to get implicit scaling information
155
+ //
156
+ for (i = 0; i < size; ++i) {
157
+ for (largest = 0.0, j = 0; j < size; ++j) {
158
+ if ((temp2 = Math.abs(A[i * size + j])) > largest) {
159
+ largest = temp2;
160
+ }
161
+ }
162
+
163
+ if (largest === 0.0 && numWarns++ < VTK_MAX_WARNS) {
164
+ // vtkGenericWarningMacro(<< "Unable to factor linear system");
165
+ return 0;
166
+ }
167
+ scale[i] = 1.0 / largest;
168
+ }
169
+ //
170
+ // Loop over all columns using Crout's method
171
+ //
172
+ for (j = 0; j < size; ++j) {
173
+ for (i = 0; i < j; ++i) {
174
+ sum = A[i * size + j];
175
+ for (k = 0; k < i; ++k) {
176
+ sum -= A[i * size + k] * A[k * size + j];
177
+ }
178
+ A[i * size + j] = sum;
179
+ }
180
+ //
181
+ // Begin search for largest pivot element
182
+ //
183
+ for (largest = 0.0, i = j; i < size; ++i) {
184
+ sum = A[i * size + j];
185
+ for (k = 0; k < j; ++k) {
186
+ sum -= A[i * size + k] * A[k * size + j];
187
+ }
188
+ A[i * size + j] = sum;
189
+
190
+ if ((temp1 = scale[i] * Math.abs(sum)) >= largest) {
191
+ largest = temp1;
192
+ maxI = i;
193
+ }
194
+ }
195
+ //
196
+ // Check for row interchange
197
+ //
198
+ if (j !== maxI) {
199
+ for (k = 0; k < size; ++k) {
200
+ temp1 = A[maxI * size + k];
201
+ A[maxI * size + k] = A[j * size + k];
202
+ A[j * size + k] = temp1;
203
+ }
204
+ scale[maxI] = scale[j];
205
+ }
206
+ //
207
+ // Divide by pivot element and perform elimination
208
+ //
209
+ index[j] = maxI;
210
+
211
+ if (Math.abs(A[j * size + j]) <= SMALL_NUMBER && numWarns++ < VTK_MAX_WARNS) {
212
+ //vtkGenericWarningMacro(<< "Unable to factor linear system");
213
+ return 0;
214
+ }
215
+
216
+ if (j !== (size - 1)) {
217
+ temp1 = 1.0 / A[j * size + j];
218
+ for (i = j + 1; i < size; ++i) {
219
+ A[i * size + j] *= temp1;
220
+ }
221
+ }
222
+ }
223
+
224
+ return 1;
225
+ }
226
+
227
+ /**
228
+ * Solve linear equations Ax = b using LU decomposition A = LU where L is
229
+ * lower triangular matrix and U is upper triangular matrix. Input is
230
+ * factored matrix A=LU, integer array of pivot indices index[0->n-1],
231
+ * load vector x[0->n-1], and size of square matrix n. Note that A=LU and
232
+ * index[] are generated from method LUFactorLinearSystem). Also, solution
233
+ * vector is written directly over input load vector.
234
+ * @param {number[]} A
235
+ * @param {number[]} index
236
+ * @param {number[]} x
237
+ * @param {number} size
238
+ */
239
+ function luf_solve_linear_system(A, index, x, size) {
240
+ let i, j, ii, idx;
241
+ let sum;
242
+ //
243
+ // Proceed with forward and backsubstitution for L and U
244
+ // matrices. First, forward substitution.
245
+ //
246
+ for (ii = -1, i = 0; i < size; ++i) {
247
+ idx = index[i];
248
+ sum = x[idx];
249
+ x[idx] = x[i];
250
+
251
+ if (ii >= 0) {
252
+ for (j = ii; j <= (i - 1); ++j) {
253
+ sum -= A[i * size + j] * x[j];
254
+ }
255
+ } else if (sum !== 0.0) {
256
+ ii = i;
257
+ }
258
+
259
+ x[i] = sum;
260
+ }
261
+ //
262
+ // Now, back substitution
263
+ //
264
+ for (i = size - 1; i >= 0; i--) {
265
+ sum = x[i];
266
+ for (j = i + 1; j < size; ++j) {
267
+ sum -= A[i * size + j] * x[j];
268
+ }
269
+ x[i] = sum / A[i * size + i];
270
+ }
271
+ }
@@ -0,0 +1,50 @@
1
+ //
2
+
3
+ import { compute_bounding_simplex_3d } from "./compute_bounding_simplex_3d.js";
4
+ import { TetrahedraCollection } from "./tetrahedra_collection.js";
5
+
6
+ /**
7
+ * @see https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
8
+ * @see [1] "One machine, one minute, three billion tetrahedra" by Célestin Marot et al
9
+ * @param {number[]} input serialized set of 3d points (x,y,z)
10
+ * @param {number} n number of points in the input
11
+ */
12
+ export function compute_delaunay_tetrahedral_mesh(input, n) {
13
+ if (n < 4) {
14
+ // no mesh can be created, too few points
15
+ return;
16
+ }
17
+
18
+ /**
19
+ * 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
+ * From the book of Cheng et al. 'Delaunay Mesh Generation' A triangulation of n vertices in R^3 has at most `ntet=(n^2 − 3n − 2)/2` tetrahedra,
21
+ * at most nedg=(n^2 − n)/2` edges (this is simply the 2-combination of n)
22
+ * and at most `ntri=n^2 − 3n = 2*ntet+2` triangles
23
+ *
24
+ * The +7 is for the initial "super" tetrahedron and 6 tetras for intermediate triangulation
25
+ */
26
+ const tetrahedron_count_upper_bound = Math.ceil((n * n - 3 * n - 2) / 2) + 7;
27
+ const tetrahedrons = new TetrahedraCollection(tetrahedron_count_upper_bound);
28
+ // TODO apply spatial sort to point, to get better cache locality
29
+
30
+ // compute bounds over the input
31
+ const points = new Float32Array((n + 4) * 3);
32
+
33
+ points.set(input);
34
+
35
+ /*
36
+ create super bounding volume
37
+ */
38
+ compute_bounding_simplex_3d(points, n * 3, points, n, 1);
39
+
40
+ tetrahedrons.append(points, n, n + 1, n + 2, n + 3);
41
+
42
+ // add all points to the mesh
43
+ for (let i = 0; i < n; i++) {
44
+ tetrahedrons.insertPoint(points, i);
45
+ }
46
+
47
+ // remove tetras formed by with the super bounding volume
48
+ tetrahedrons.removeTetrasConnectedToPoints(points, n, n + 3);
49
+ }
50
+
@@ -0,0 +1,14 @@
1
+ import { compute_delaunay_tetrahedral_mesh } from "./compute_delaunay_tetrahedral_mesh.js";
2
+
3
+ test('single point', () => {
4
+ compute_delaunay_tetrahedral_mesh([1, 2, 3], 1);
5
+ });
6
+
7
+ test('exactly 4 points', () => {
8
+ compute_delaunay_tetrahedral_mesh([
9
+ -1, 0, 0,
10
+ 1, 0, 0,
11
+ 0.5, 1, 0,
12
+ 0.5, 0, 1
13
+ ], 4);
14
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Approximate 3D insphere test. Nonrobust.
3
+ *
4
+ * Return a positive value if the point pe lies inside the
5
+ * sphere passing through pa, pb, pc, and pd; a negative value
6
+ * if it lies outside; and zero if the five points are
7
+ * cospherical. The points pa, pb, pc, and pd must be ordered
8
+ * so that they have a positive orientation (as defined by
9
+ * orient3d()), or the sign of the result will be reversed.
10
+ *
11
+ * @see "Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates." Technical Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996
12
+ * @see http://www.cs.cmu.edu/~quake/robust.html
13
+ * @see https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/contrib/hxt/predicates/src/predicates.c
14
+ * @param {number[]} points
15
+ * @param {number} a tetrahedral point index
16
+ * @param {number} b tetrahedral point index
17
+ * @param {number} c tetrahedral point index
18
+ * @param {number} d tetrahedral point index
19
+ * @param {number} e reference point index
20
+ * @returns {number}
21
+ */
22
+ export function point_in_tetrahedron_circumsphere(points, a, b, c, d, e) {
23
+ const ex = points[e * 3];
24
+ const ey = points[e * 3 + 1];
25
+ const ez = points[e * 3 + 2];
26
+
27
+ const aex = points[a * 3] - ex;
28
+ const bex = points[b * 3] - ex;
29
+ const cex = points[c * 3] - ex;
30
+ const dex = points[d * 3] - ex;
31
+
32
+ const aey = points[a * 3 + 1] - ey;
33
+ const bey = points[b * 3 + 1] - ey;
34
+ const cey = points[c * 3 + 1] - ey;
35
+ const dey = points[d * 3 + 1] - ey;
36
+
37
+ const aez = points[a * 3 + 2] - ez;
38
+ const bez = points[b * 3 + 2] - ez;
39
+ const cez = points[c * 3 + 2] - ez;
40
+ const dez = points[d * 3 + 1] - ez;
41
+
42
+ const aexbey = aex * bey;
43
+ const bexaey = bex * aey;
44
+ const ab = aexbey - bexaey;
45
+ const bexcey = bex * cey;
46
+ const cexbey = cex * bey;
47
+ const bc = bexcey - cexbey;
48
+ const cexdey = cex * dey;
49
+ const dexcey = dex * cey;
50
+ const cd = cexdey - dexcey;
51
+ const dexaey = dex * aey;
52
+ const aexdey = aex * dey;
53
+ const da = dexaey - aexdey;
54
+
55
+ const aexcey = aex * cey;
56
+ const cexaey = cex * aey;
57
+ const ac = aexcey - cexaey;
58
+ const bexdey = bex * dey;
59
+ const dexbey = dex * bey;
60
+ const bd = bexdey - dexbey;
61
+
62
+ const abc = aez * bc - bez * ac + cez * ab;
63
+ const bcd = bez * cd - cez * bd + dez * bc;
64
+ const cda = cez * da + dez * ac + aez * cd;
65
+ const dab = dez * ab + aez * bd + bez * da;
66
+
67
+ const alift = aex * aex + aey * aey + aez * aez;
68
+ const blift = bex * bex + bey * bey + bez * bez;
69
+ const clift = cex * cex + cey * cey + cez * cez;
70
+ const dlift = dex * dex + dey * dey + dez * dez;
71
+
72
+ const det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
73
+
74
+ // TODO HXT uses 2 more methods to perform exact calculation (avoiding rounding errors), we could include that if necessary
75
+
76
+ return det;
77
+ }