@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.
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 +5 -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
@@ -0,0 +1,30 @@
1
+ import { TetrahedralMesh } from "../TetrahedralMesh.js";
2
+ import { tetrahedral_mesh_compute_sub_determinant } from "./tetrahedral_mesh_compute_sub_determinant.js";
3
+
4
+ test("sanity test", () => {
5
+ const mesh = new TetrahedralMesh(1);
6
+
7
+ const tet = mesh.allocate();
8
+
9
+ mesh.setVertexIndex(tet, 0, 0);
10
+ mesh.setVertexIndex(tet, 1, 1);
11
+ mesh.setVertexIndex(tet, 2, 2);
12
+ mesh.setVertexIndex(tet, 3, 3);
13
+
14
+ const result = [];
15
+
16
+ const points = [
17
+ 1, 2, 3,
18
+ 4, 5, 6,
19
+ 7, 8, 9,
20
+ 10, 11, 12
21
+ ];
22
+
23
+ tetrahedral_mesh_compute_sub_determinant(result, 0, mesh, points, tet);
24
+
25
+ for (let i = 0; i < 4; i++) {
26
+ expect(typeof result[i]).toBe("number");
27
+ expect(result[i]).not.toBeNaN();
28
+ }
29
+
30
+ });
@@ -0,0 +1,98 @@
1
+ import { tetrahedral_mesh_compute_cavity } from "./tetrahedral_mesh_compute_cavity.js";
2
+ import { assert } from "../../../../assert.js";
3
+ import { Cavity } from "./Cavity.js";
4
+ import { INVALID_NEIGHBOUR, LAYOUT_TETRA_BYTE_SIZE } from "../TetrahedralMesh.js";
5
+
6
+ const SCRATCH_CAVITY = new Cavity();
7
+
8
+ /**
9
+ *
10
+ * @param {TetrahedralMesh} mesh
11
+ * @param {number[]|Float32Array} points
12
+ * @param {number} index
13
+ */
14
+ export function tetrahedral_mesh_insert_point(mesh, points, index) {
15
+ const debug_length_initial = mesh.__length;
16
+
17
+ // Compute the cavity, that is the set of tetrahedra that need to be removed and rebuilt
18
+ tetrahedral_mesh_compute_cavity(mesh, SCRATCH_CAVITY, points, index);
19
+
20
+
21
+ // identify cavity "edge" - the set of tetras that are neighbours of the tetras that form the cavity
22
+ const cavity_size = SCRATCH_CAVITY.__deleted_size;
23
+ const cavity_indices = SCRATCH_CAVITY.__deleted;
24
+
25
+ for (let i = 0; i < cavity_size; i++) {
26
+ const data = mesh.__view;
27
+
28
+ const cavity_index = cavity_indices[i];
29
+
30
+ // take each side of the tetra
31
+ const tetra_address = cavity_index * LAYOUT_TETRA_BYTE_SIZE;
32
+
33
+ const a = data.getUint32(tetra_address);
34
+ const b = data.getUint32(tetra_address + 4);
35
+ const c = data.getUint32(tetra_address + 8);
36
+ const d = data.getUint32(tetra_address + 12);
37
+
38
+ // attempt to form 4 tetras using the sides of the cavity
39
+ const a_neighbour = data.getUint32(tetra_address + 16);
40
+ const a_neighbour_index = a_neighbour >> 2;
41
+
42
+ const b_neighbour = data.getUint32(tetra_address + 20);
43
+ const b_neighbour_index = b_neighbour >> 2;
44
+
45
+ const c_neighbour = data.getUint32(tetra_address + 24);
46
+ const c_neighbour_index = c_neighbour >> 2;
47
+
48
+ const d_neighbour = data.getUint32(tetra_address + 28);
49
+ const d_neighbour_index = d_neighbour >> 2;
50
+
51
+
52
+ if (a_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(a_neighbour_index) === 0) {
53
+ // ABC is a valid side
54
+ const tet = mesh.append(points, b, d, c, index);
55
+ // patch in neighbours
56
+ if (a_neighbour !== INVALID_NEIGHBOUR) {
57
+ mesh.setNeighbour(tet, 3, a_neighbour);
58
+ mesh.setNeighbour(a_neighbour_index, a_neighbour & 3, tet);
59
+ }
60
+ }
61
+ if (b_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(b_neighbour_index) === 0) {
62
+ // ABC is a valid side
63
+ const tet = mesh.append(points, c, d, a, index);
64
+ if (b_neighbour !== INVALID_NEIGHBOUR) {
65
+ mesh.setNeighbour(tet, 3, b_neighbour);
66
+ mesh.setNeighbour(b_neighbour_index, b_neighbour & 3, tet);
67
+ }
68
+ }
69
+ if (c_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(c_neighbour_index) === 0) {
70
+ // ABC is a valid side
71
+ const tet = mesh.append(points, d, b, a, index);
72
+ if (c_neighbour !== INVALID_NEIGHBOUR) {
73
+ mesh.setNeighbour(tet, 3, c_neighbour);
74
+ mesh.setNeighbour(c_neighbour_index, c_neighbour & 3, tet);
75
+ }
76
+ }
77
+ if (d_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(d_neighbour_index) === 0) {
78
+ // ABC is a valid side
79
+ const tet = mesh.append(points, a, b, c, index);
80
+ if (d_neighbour !== INVALID_NEIGHBOUR) {
81
+ mesh.setNeighbour(tet, 3, d_neighbour);
82
+ mesh.setNeighbour(d_neighbour_index, d_neighbour & 3, tet);
83
+ }
84
+ }
85
+ }
86
+
87
+
88
+ // 3. remove the cavity tetrahedrons
89
+ for (let i = 0; i < cavity_size; i++) {
90
+ const cavity_index = cavity_indices[i];
91
+
92
+ // clear out cavity, connectivity was already patched in the previous step
93
+ mesh.__occupancy.set(cavity_index, false);
94
+ mesh.__length--;
95
+ }
96
+
97
+ assert.greaterThan(mesh.__length, debug_length_initial, 'after insertion number of tetrahedra must grow, not shrink');
98
+ }
@@ -1,4 +1,6 @@
1
1
  import { orient3d_fast } from "../../plane/orient3d_fast.js";
2
+ import { assert } from "../../../../assert.js";
3
+ import { INVALID_NEIGHBOUR } from "../TetrahedralMesh.js";
2
4
 
3
5
  /**
4
6
  * Walk from a given tetrahedron in the mesh towards tetrahedron that contains input point
@@ -7,12 +9,12 @@ import { orient3d_fast } from "../../plane/orient3d_fast.js";
7
9
  * @see https://git.immc.ucl.ac.be/hextreme/hxt_seqdel/-/blob/master/src/hxt_tetrahedra.c#L330
8
10
  * @see p3 "One machine, one minute, three billion tetrahedra" by Célestin Marot, Jeanne Pellerin, Jean-François Remacle
9
11
  * @param {TetrahedralMesh} mesh
10
- * @param {number[]} points
12
+ * @param {number[]|Float32Array} points
11
13
  * @param {number} current_tet
12
14
  * @param {number} current_vertex
13
15
  * @returns {number}
14
16
  */
15
- function tetrahedral_mesh_walk_toward_cavity(mesh, points, current_tet, current_vertex) {
17
+ export function tetrahedral_mesh_walk_toward_cavity(mesh, points, current_tet, current_vertex) {
16
18
 
17
19
  let entering_face = 4;
18
20
 
@@ -28,19 +30,24 @@ function tetrahedral_mesh_walk_toward_cavity(mesh, points, current_tet, current_
28
30
  const b_i = (i & 2) ^ 3;
29
31
  const c_i = (i + 3) & 2;
30
32
 
31
- const a_index = mesh.getCornerIndex(curTet, a_i);
32
- const b_index = mesh.getCornerIndex(curTet, b_i);
33
- const c_index = mesh.getCornerIndex(curTet, c_i);
33
+ const a_index = mesh.getVertexIndex(curTet, a_i);
34
+ const b_index = mesh.getVertexIndex(curTet, b_i);
35
+ const c_index = mesh.getVertexIndex(curTet, c_i);
34
36
 
35
37
  if (i !== entering_face && orient3d_fast(points, a_index, b_index, c_index, current_vertex) < 0.0) {
38
+ // point is outside the tet on the neighbour's side, move in that direction
36
39
  const neighbour = mesh.getNeighbour(curTet, i);
37
- curTet = neighbour >> 2;
40
+
41
+ assert.notEqual(neighbour, INVALID_NEIGHBOUR, 'walked outside of the mesh');
42
+
43
+ curTet = neighbour >>> 2;
38
44
  entering_face = neighbour & 3;
39
45
  break;
40
46
  }
41
47
  }
42
48
 
43
49
  if (i === 4) {
50
+ // point is inside the tet
44
51
  return curTet;
45
52
  }
46
53
  }
@@ -34,20 +34,20 @@ export function point_in_tetrahedron_circumsphere(
34
34
  const ey = points[e3 + 1];
35
35
  const ez = points[e3 + 2];
36
36
 
37
-
38
37
  const aex = points[a3] - ex;
39
- const bex = points[b3] - ex;
40
- const cex = points[c3] - ex;
41
- const dex = points[d3] - ex;
42
-
43
38
  const aey = points[a3 + 1] - ey;
44
- const bey = points[b3 + 1] - ey;
45
- const cey = points[c3 + 1] - ey;
46
- const dey = points[d3 + 1] - ey;
47
-
48
39
  const aez = points[a3 + 2] - ez;
40
+
41
+ const bex = points[b3] - ex;
42
+ const bey = points[b3 + 1] - ey;
49
43
  const bez = points[b3 + 2] - ez;
44
+
45
+ const cex = points[c3] - ex;
46
+ const cey = points[c3 + 1] - ey;
50
47
  const cez = points[c3 + 2] - ez;
48
+
49
+ const dex = points[d3] - ex;
50
+ const dey = points[d3 + 1] - ey;
51
51
  const dez = points[d3 + 2] - ez;
52
52
 
53
53
  const aexbey = aex * bey;
@@ -10,7 +10,7 @@ import { Transform } from "../../../../engine/ecs/transform/Transform.js";
10
10
  import { ShadedGeometrySystem } from "../../../../engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
11
11
  import { GizmoRenderingPlugin } from "../../../../engine/graphics/render/gizmo/GizmoRenderingPlugin.js";
12
12
  import { Gizmo } from "../../../../engine/graphics/render/gizmo/Gizmo.js";
13
- import { LAYOUT_TETRA_BYTE_SIZE } from "./tetrahedra_collection.js";
13
+ import { LAYOUT_TETRA_BYTE_SIZE } from "./TetrahedralMesh.js";
14
14
 
15
15
  /**
16
16
  *
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Compute signed volume of a tetrahedron with the last point at the origin
3
+ * @see "EFFICIENT FEATURE EXTRACTION FOR 2D/3D OBJECTS IN MESH REPRESENTATION" by Cha Zhang and Tsuhan Chen
4
+ * @param {number} ax
5
+ * @param {number} ay
6
+ * @param {number} az
7
+ * @param {number} bx
8
+ * @param {number} by
9
+ * @param {number} bz
10
+ * @param {number} cx
11
+ * @param {number} cy
12
+ * @param {number} cz
13
+ * @return {number}
14
+ */
15
+ function triangle_compute_signed_volume(
16
+ ax, ay, az,
17
+ bx, by, bz,
18
+ cx, cy, cz
19
+ ) {
20
+
21
+ const v321 = cx * by * az;
22
+ const v231 = bx * cy * az;
23
+ const v312 = cx * ay * bz;
24
+ const v132 = ax * cy * bz;
25
+ const v213 = bx * ay * cz;
26
+ const v123 = ax * by * cz;
27
+
28
+ return (1.0 / 6.0) * (-v321 + v231 + v312 - v132 - v213 + v123);
29
+
30
+ }
31
+
32
+
33
+ /**
34
+ * @see https://en.wikipedia.org/wiki/Tetrahedron
35
+ *
36
+ * @param {ArrayLike<number>|number[]|Float32Array} points
37
+ * @param {number} a
38
+ * @param {number} b
39
+ * @param {number} c
40
+ * @param {number} d
41
+ * @returns {number}
42
+ */
43
+ export function tetrahedron_compute_signed_volume(points, a, b, c, d) {
44
+
45
+ const a3 = a * 3;
46
+ const b3 = b * 3;
47
+ const c3 = c * 3;
48
+ const d3 = d * 3;
49
+
50
+ const ax = points[a3];
51
+ const ay = points[a3 + 1];
52
+ const az = points[a3 + 2];
53
+
54
+ const bx = points[b3];
55
+ const by = points[b3 + 1];
56
+ const bz = points[b3 + 2];
57
+
58
+ const cx = points[c3];
59
+ const cy = points[c3 + 1];
60
+ const cz = points[c3 + 2];
61
+
62
+ const dx = points[d3];
63
+ const dy = points[d3 + 1];
64
+ const dz = points[d3 + 2];
65
+
66
+ const adx = ax - dx;
67
+ const ady = ay - dy;
68
+ const adz = az - dz;
69
+
70
+ const bdx = bx - dx;
71
+ const bdy = by - dy;
72
+ const bdz = bz - dz;
73
+
74
+ const cdx = cx - dx;
75
+ const cdy = cy - dy;
76
+ const cdz = cz - dz;
77
+
78
+ return triangle_compute_signed_volume(
79
+ adx, ady, adz,
80
+ bdx, bdy, bdz,
81
+ cdx, cdy, cdz
82
+ );
83
+ }
@@ -0,0 +1,24 @@
1
+ import { tetrahedron_compute_signed_volume } from "./tetrahedron_compute_signed_volume.js";
2
+
3
+ test("0 volume", () => {
4
+ expect(tetrahedron_compute_signed_volume([
5
+ 0, 0, 0
6
+ ], 0, 0, 0, 0)).toBe(0);
7
+ });
8
+
9
+ test("positive volume", () => {
10
+
11
+ expect(tetrahedron_compute_signed_volume([
12
+ 1, 0, 1,
13
+ 1, 0, 0,
14
+ 0, 0, 0,
15
+ 1, 1, 1
16
+ ], 1, 2, 3, 0)).toBeGreaterThan(0);
17
+
18
+ expect(tetrahedron_compute_signed_volume([
19
+ 0, 0, 0,
20
+ 2, 0, 0,
21
+ 0, 2, 0,
22
+ 0, 0, 2
23
+ ], 1, 2, 3, 0)).toBeGreaterThan(0);
24
+ })
@@ -0,0 +1,66 @@
1
+ import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
2
+
3
+ test("basic containment", () => {
4
+ const tetrahedron = [
5
+ 1, 0, 1,
6
+ 1, 0, 0,
7
+ 0, 0, 0,
8
+ 1, 1, 1
9
+ ];
10
+
11
+ expect(
12
+ tetrahedron_contains_point([
13
+ ...tetrahedron,
14
+ //
15
+ 0.5, 0.1, 0.5
16
+ ], 0, 1, 2, 3, 4)
17
+ ).toBe(true);
18
+
19
+ expect(
20
+ tetrahedron_contains_point([
21
+ ...tetrahedron,
22
+ //
23
+ -1, 0, 0
24
+ ], 0, 1, 2, 3, 4)
25
+ ).toBe(false);
26
+
27
+ expect(
28
+ tetrahedron_contains_point([
29
+ ...tetrahedron,
30
+ //
31
+ 2, 0, 0
32
+ ], 0, 1, 2, 3, 4)
33
+ ).toBe(false);
34
+
35
+ expect(
36
+ tetrahedron_contains_point([
37
+ ...tetrahedron,
38
+ //
39
+ 0, -1, 0
40
+ ], 0, 1, 2, 3, 4)
41
+ ).toBe(false);
42
+
43
+ expect(
44
+ tetrahedron_contains_point([
45
+ ...tetrahedron,
46
+ //
47
+ 0, 2, 0
48
+ ], 0, 1, 2, 3, 4)
49
+ ).toBe(false);
50
+
51
+ expect(
52
+ tetrahedron_contains_point([
53
+ ...tetrahedron,
54
+ //
55
+ 0, 0, -1
56
+ ], 0, 1, 2, 3, 4)
57
+ ).toBe(false);
58
+
59
+ expect(
60
+ tetrahedron_contains_point([
61
+ ...tetrahedron,
62
+ //
63
+ 0, 0, 2
64
+ ], 0, 1, 2, 3, 4)
65
+ ).toBe(false);
66
+ });
@@ -0,0 +1,119 @@
1
+ import { tetrahedron_compute_signed_volume } from "./tetrahedron_compute_signed_volume.js";
2
+ import { noop } from "../../../function/Functions.js";
3
+ import { INVALID_NEIGHBOUR } from "./TetrahedralMesh.js";
4
+
5
+ /**
6
+ *
7
+ * @param {TetrahedralMesh} mesh
8
+ * @param {ArrayLike<number>|number[]} points
9
+ * @param {number} tet
10
+ * @return {number}
11
+ */
12
+ function get_tetrahedron_volume(mesh, points, tet) {
13
+
14
+ const a = mesh.getVertexIndex(tet, 0);
15
+ const b = mesh.getVertexIndex(tet, 1);
16
+ const c = mesh.getVertexIndex(tet, 2);
17
+ const d = mesh.getVertexIndex(tet, 3);
18
+
19
+ return tetrahedron_compute_signed_volume(points, a, b, c, d);
20
+ }
21
+
22
+ /**
23
+ *
24
+ * @param {TetrahedralMesh} mesh
25
+ * @param {number} tet
26
+ * @param {function(problem:string):*} consumer
27
+ */
28
+ function validate_tetrahedron_neighbourhood(mesh, tet, consumer = noop) {
29
+ let valid = true;
30
+
31
+ for (let i = 0; i < 4; i++) {
32
+ const neighbour = mesh.getNeighbour(tet, i);
33
+
34
+ if (neighbour === INVALID_NEIGHBOUR) {
35
+ continue;
36
+ }
37
+
38
+ // decode neighbour
39
+ const neighbour_tet = neighbour >> 2;
40
+ const neighbour_vertex_id = neighbour & 3;
41
+
42
+ if (!mesh.exists(neighbour_tet)) {
43
+ valid = false;
44
+
45
+ consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} does not exist in the mesh`);
46
+
47
+ continue;
48
+ }
49
+
50
+ const back_link = mesh.getNeighbour(neighbour_tet, neighbour_vertex_id);
51
+
52
+ if (back_link === INVALID_NEIGHBOUR) {
53
+ valid = false;
54
+ consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to invalid neighbour (no neighbour), expected ${tet} instead`);
55
+ continue;
56
+ }
57
+
58
+ const back_link_tet = back_link >> 2;
59
+ const back_link_vertex_index = back_link & 3;
60
+
61
+ if (back_link_tet !== tet) {
62
+ valid = false;
63
+
64
+ consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to a different tet, expected ${tet}, got ${back_link_tet}`)
65
+ } else if (back_link_vertex_index !== i) {
66
+ valid = false;
67
+
68
+ consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to a different vertex, expected ${i}, got ${back_link_vertex_index}`);
69
+
70
+ }
71
+
72
+ for (let j = 0; j < 4; j++) {
73
+ const v = mesh.getVertexIndex(tet, j);
74
+
75
+ if (j === i) {
76
+ continue;
77
+ }
78
+
79
+ if (!mesh.tetContainsVertex(neighbour_tet, v)) {
80
+ valid = false;
81
+
82
+ consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} does not share vertex ${v} with the tet`);
83
+
84
+ }
85
+ }
86
+
87
+ }
88
+
89
+ return valid;
90
+ }
91
+
92
+ /**
93
+ *
94
+ * @param {TetrahedralMesh} mesh
95
+ * @param {ArrayLike<number>} points
96
+ * @param {(problem:string)=>*} [consumer]
97
+ * @returns {boolean}
98
+ */
99
+ export function validate_tetrahedral_mesh(mesh, points, consumer = noop) {
100
+ let is_valid = true;
101
+
102
+ mesh.forEach((tet, mesh) => {
103
+
104
+ const volume = get_tetrahedron_volume(mesh, points, tet);
105
+ if (volume < 0) {
106
+ is_valid = false;
107
+
108
+ consumer(`Tetrahedron ${tet} has negative volume ${volume}`);
109
+ }
110
+
111
+ if (!validate_tetrahedron_neighbourhood(mesh, tet, consumer)) {
112
+ is_valid = false;
113
+ }
114
+
115
+ });
116
+
117
+
118
+ return is_valid;
119
+ }
@@ -1,30 +1,3 @@
1
- import Vector3 from "./Vector3.js";
2
-
3
- export function Bezier() {
4
-
5
- }
6
-
7
- Bezier.computeQuadratic = (function () {
8
- const a = new Vector3();
9
-
10
- function computeQuadratic(p0, p1, p2, t, result) {
11
- const minus = 1 - t;
12
- const minus2 = minus * minus;
13
-
14
- result.copy(p0).multiplyScalar(minus2);
15
-
16
- a.copy(p1).multiplyScalar(minus * 2 * t);
17
- result.add(a);
18
-
19
- a.copy(p2).multiplyScalar(t * t);
20
- result.add(a);
21
-
22
- }
23
-
24
- return computeQuadratic;
25
- })();
26
-
27
-
28
1
  /**
29
2
  *
30
3
  * @param {Vector2} result
@@ -5,10 +5,6 @@
5
5
 
6
6
  import { v3_dot } from "./v3_dot.js";
7
7
 
8
- function Plane() {
9
-
10
- }
11
-
12
8
  /**
13
9
 
14
10
  Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
@@ -40,13 +40,13 @@ export class Subspan {
40
40
 
41
41
  /**
42
42
  * DxD square matrix, where D is number of dimensions
43
- * @type {[]}
43
+ * @type {number[][]}
44
44
  */
45
45
  this.Q = null;
46
46
 
47
47
  /**
48
48
  * DxD square matrix, where D is number of dimensions
49
- * @type {[]}
49
+ * @type {number[][]}
50
50
  */
51
51
  this.R = null;
52
52
 
@@ -11,7 +11,12 @@ import { lerp } from "../math/lerp.js";
11
11
  * @param {number} b_z
12
12
  * @param {number} fraction
13
13
  */
14
- export function v3_lerp(result, a_x, a_y, a_z, b_x, b_y, b_z, fraction) {
14
+ export function v3_lerp(
15
+ result,
16
+ a_x, a_y, a_z,
17
+ b_x, b_y, b_z,
18
+ fraction
19
+ ) {
15
20
 
16
21
  const x = lerp(a_x, b_x, fraction);
17
22
  const y = lerp(a_y, b_y, fraction);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Integer square root
3
+ *
4
+ * @author Jim Ulery (attributed on hextreme)
5
+ * @see https://en.wikipedia.org/wiki/Integer_square_root
6
+ * @see https://git.immc.ucl.ac.be/hextreme/hxt_seqdel/-/blob/master/src/hxt_tetrahedra.c#L54
7
+ *
8
+ * @param {number} input
9
+ * @returns {number}
10
+ */
11
+ export function isqrt(input) {
12
+ let temp;
13
+
14
+ let g = 0;
15
+ let b = 0x8000;
16
+ let bshft = 15;
17
+
18
+ let val = input;
19
+
20
+ do {
21
+ if (val >= (temp = (((g << 1) + b) << bshft--))) {
22
+ g += b;
23
+ val -= temp;
24
+ }
25
+ } while (b >>= 1);
26
+
27
+ return g;
28
+ }
@@ -0,0 +1,9 @@
1
+ import { isqrt } from "./isqrt.js";
2
+
3
+ test('1', () => {
4
+ expect(isqrt(1)).toBe(1);
5
+ });
6
+
7
+ test('4', () => {
8
+ expect(isqrt(4)).toBe(2);
9
+ });
@@ -0,0 +1,25 @@
1
+ import { max2 } from "./max2.js";
2
+
3
+ test("0,0", () => {
4
+ expect(max2(0, 0)).toBe(0);
5
+ });
6
+
7
+ test("0,1", () => {
8
+ expect(max2(0, 1)).toBe(1);
9
+ });
10
+
11
+ test("1,0", () => {
12
+ expect(max2(1, 0)).toBe(1);
13
+ });
14
+
15
+ test("-1,0", () => {
16
+ expect(max2(-1, 0)).toBe(0);
17
+ });
18
+
19
+ test("0,-1", () => {
20
+ expect(max2(0, -1)).toBe(0);
21
+ });
22
+
23
+ test("Infinity,-Infinity", () => {
24
+ expect(max2(Infinity, -Infinity)).toBe(Infinity);
25
+ });
@@ -0,0 +1,25 @@
1
+ import { min2 } from "./min2.js";
2
+
3
+ test("0,0", () => {
4
+ expect(min2(0, 0)).toBe(0);
5
+ });
6
+
7
+ test("0,1", () => {
8
+ expect(min2(0, 1)).toBe(0);
9
+ });
10
+
11
+ test("1,0", () => {
12
+ expect(min2(1, 0)).toBe(0);
13
+ });
14
+
15
+ test("-1,0", () => {
16
+ expect(min2(-1, 0)).toBe(-1);
17
+ });
18
+
19
+ test("0,-1", () => {
20
+ expect(min2(0, -1)).toBe(-1);
21
+ });
22
+
23
+ test("Infinity,-Infinity", () => {
24
+ expect(min2(Infinity, -Infinity)).toBe(-Infinity);
25
+ });