@woosh/meep-engine 2.133.5 → 2.134.0

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/build/bundle-worker-image-decoder.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/BinaryBuffer.d.ts +20 -0
  4. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  5. package/src/core/binary/BinaryBuffer.js +50 -0
  6. package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.d.ts +4 -4
  7. package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.d.ts.map +1 -1
  8. package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.js +2 -2
  9. package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.d.ts +2 -2
  10. package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.d.ts.map +1 -1
  11. package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.js +1 -1
  12. package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.d.ts +2 -2
  13. package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.d.ts.map +1 -1
  14. package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.js +1 -1
  15. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts +4 -4
  16. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts.map +1 -1
  17. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +3 -3
  18. package/src/core/geom/3d/topology/simplify/quadratic/{Quadratic3.d.ts → Quadric3.d.ts} +55 -29
  19. package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.d.ts.map +1 -0
  20. package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.js +307 -0
  21. package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.d.ts +7 -7
  22. package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.d.ts.map +1 -1
  23. package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.js +39 -13
  24. package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.d.ts +2 -2
  25. package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.d.ts.map +1 -1
  26. package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.js +1 -1
  27. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.d.ts +2 -2
  28. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.d.ts.map +1 -1
  29. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.js +2 -2
  30. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.d.ts +4 -4
  31. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.d.ts.map +1 -1
  32. package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +5 -5
  33. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts +8 -0
  34. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts.map +1 -0
  35. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -0
  36. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.d.ts +14 -0
  37. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.d.ts.map +1 -0
  38. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +95 -0
  39. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +17 -0
  40. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -0
  41. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +352 -0
  42. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.d.ts +21 -0
  43. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.d.ts.map +1 -0
  44. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -0
  45. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts +16 -0
  46. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts.map +1 -0
  47. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +90 -0
  48. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.d.ts +19 -0
  49. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.d.ts.map +1 -0
  50. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -0
  51. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.d.ts +22 -0
  52. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.d.ts.map +1 -0
  53. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +148 -0
  54. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.js +4 -4
  55. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.d.ts +15 -0
  56. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.d.ts.map +1 -0
  57. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +48 -0
  58. package/src/engine/navigation/mesh/NavigationMesh.d.ts +6 -4
  59. package/src/engine/navigation/mesh/NavigationMesh.d.ts.map +1 -1
  60. package/src/engine/navigation/mesh/NavigationMesh.js +212 -190
  61. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  62. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +20 -7
  63. package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts +15 -0
  64. package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts.map +1 -0
  65. package/src/engine/navigation/mesh/bvh_query_nearest_face.js +131 -0
  66. package/src/core/geom/3d/topology/simplify/quadratic/Quadratic3.d.ts.map +0 -1
  67. package/src/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +0 -302
@@ -8,10 +8,10 @@ export function extract_edge_cost(edge: EdgeCollapseCandidate): number;
8
8
  * @param {BinaryHeap<EdgeCollapseCandidate>} heap
9
9
  * @param {TopoMesh} mesh
10
10
  * @param {Map<TopoEdge, EdgeCollapseCandidate>} edge_to_collapse_map
11
- * @param {Map<number, Quadratic3>} vertex_quadratics
11
+ * @param {Map<number, Quadric3>} vertex_quadratics
12
12
  * @param {Set<number>} restricted_vertices
13
13
  */
14
- export function collapse_edges(number_faces_to_remove: number, heap: BinaryHeap<EdgeCollapseCandidate>, mesh: TopoMesh, edge_to_collapse_map: Map<TopoEdge, EdgeCollapseCandidate>, vertex_quadratics: Map<number, Quadratic3>, restricted_vertices: Set<number>): void;
14
+ export function collapse_edges(number_faces_to_remove: number, heap: BinaryHeap<EdgeCollapseCandidate>, mesh: TopoMesh, edge_to_collapse_map: Map<TopoEdge, EdgeCollapseCandidate>, vertex_quadratics: Map<number, Quadric3>, restricted_vertices: Set<number>): void;
15
15
  /**
16
16
  * Simplifies a given topology mesh by reducing number of faces. Preserves original vertices
17
17
  * NOTE: preserves outline of the mesh, that is the open edge
@@ -1 +1 @@
1
- {"version":3,"file":"simplifyTopoMesh.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/topology/simplify/simplifyTopoMesh.js"],"names":[],"mappings":"AAcA;;;GAGG;AACH,wCAFW,qBAAqB,UAI/B;AAGD;;;;;;;GAOG;AACH,uDAPW,MAAM,QACN,WAAW,qBAAqB,CAAC,wCAEjC,cAAc,qBAAqB,CAAC,qBACpC,IAAI,MAAM,aAAa,uBACvB,IAAI,MAAM,CAAC,QAyKrB;AAED;;;;;;;GAOG;AACH,sEAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QAwDrB;sCAhQqC,4BAA4B;uBAL3C,2CAA2C"}
1
+ {"version":3,"file":"simplifyTopoMesh.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/topology/simplify/simplifyTopoMesh.js"],"names":[],"mappings":"AAcA;;;GAGG;AACH,wCAFW,qBAAqB,UAI/B;AAGD;;;;;;;GAOG;AACH,uDAPW,MAAM,QACN,WAAW,qBAAqB,CAAC,wCAEjC,cAAc,qBAAqB,CAAC,qBACpC,IAAI,MAAM,WAAW,uBACrB,IAAI,MAAM,CAAC,QAyKrB;AAED;;;;;;;GAOG;AACH,sEAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QAwDrB;sCAhQqC,4BAA4B;uBAL3C,2CAA2C"}
@@ -26,7 +26,7 @@ export function extract_edge_cost(edge) {
26
26
  * @param {BinaryHeap<EdgeCollapseCandidate>} heap
27
27
  * @param {TopoMesh} mesh
28
28
  * @param {Map<TopoEdge, EdgeCollapseCandidate>} edge_to_collapse_map
29
- * @param {Map<number, Quadratic3>} vertex_quadratics
29
+ * @param {Map<number, Quadric3>} vertex_quadratics
30
30
  * @param {Set<number>} restricted_vertices
31
31
  */
32
32
  export function collapse_edges(
@@ -231,7 +231,7 @@ export function simplifyTopoMesh(mesh, num_faces_to_remove, restricted_vertices
231
231
 
232
232
  /**
233
233
  *
234
- * @type {Map<number, Quadratic3>}
234
+ * @type {Map<number, Quadric3>}
235
235
  */
236
236
  const vertex_quadratics = new Map();
237
237
 
@@ -3,10 +3,10 @@
3
3
  * @param {Iterable<TopoEdge>} edges
4
4
  * @param {Set<number>} restricted_vertices
5
5
  * @param {Uint32Heap} heap
6
- * @param {Map<number, Quadratic3>} vertex_quadratics
6
+ * @param {Map<number, Quadric3>} vertex_quadratics
7
7
  * @param {Map<number,TopoEdge>} edge_lookup
8
8
  */
9
- export function edge_collapse_populate_heap(edges: Iterable<TopoEdge>, restricted_vertices: Set<number>, heap: Uint32Heap, vertex_quadratics: Map<number, Quadratic3>, edge_lookup: Map<number, TopoEdge>): void;
9
+ export function edge_collapse_populate_heap(edges: Iterable<TopoEdge>, restricted_vertices: Set<number>, heap: Uint32Heap, vertex_quadratics: Map<number, Quadric3>, edge_lookup: Map<number, TopoEdge>): void;
10
10
  /**
11
11
  *
12
12
  * @param {TopoMesh} mesh
@@ -14,9 +14,9 @@ export function edge_collapse_populate_heap(edges: Iterable<TopoEdge>, restricte
14
14
  * @param {Uint32Heap} heap
15
15
  * @param {Map<number,TopoEdge>} edge_lookup
16
16
  * @param {Set<number>} restricted_vertices
17
- * @param {Map<number, Quadratic3>} vertex_quadratics
17
+ * @param {Map<number, Quadric3>} vertex_quadratics
18
18
  */
19
- export function edge_collapse_reduce_face_count(mesh: TopoMesh, num_faces_to_remove: number, heap: Uint32Heap, edge_lookup: Map<number, TopoEdge>, restricted_vertices: Set<number>, vertex_quadratics: Map<number, Quadratic3>): void;
19
+ export function edge_collapse_reduce_face_count(mesh: TopoMesh, num_faces_to_remove: number, heap: Uint32Heap, edge_lookup: Map<number, TopoEdge>, restricted_vertices: Set<number>, vertex_quadratics: Map<number, Quadric3>): void;
20
20
  /**
21
21
  * Simplifies a given topology mesh by reducing number of faces. Preserves original vertices
22
22
  * NOTE: preserves outline of the mesh, that is the open edge
@@ -1 +1 @@
1
- {"version":3,"file":"simplifyTopoMesh2.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/topology/simplify/simplifyTopoMesh2.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,mDANW,kBAAkB,uBAClB,IAAI,MAAM,CAAC,QACX,UAAU,qBACV,IAAI,MAAM,aAAa,eACvB,IAAI,MAAM,WAAU,QAsB9B;AAED;;;;;;;;GAQG;AACH,qFANW,MAAM,QACN,UAAU,eACV,IAAI,MAAM,WAAU,uBACpB,IAAI,MAAM,CAAC,qBACX,IAAI,MAAM,aAAa,QAgCjC;AAED;;;;;;;GAOG;AACH,uEAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QAgCrB;2BAtH0B,2CAA2C"}
1
+ {"version":3,"file":"simplifyTopoMesh2.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/topology/simplify/simplifyTopoMesh2.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,mDANW,kBAAkB,uBAClB,IAAI,MAAM,CAAC,QACX,UAAU,qBACV,IAAI,MAAM,WAAW,eACrB,IAAI,MAAM,WAAU,QAsB9B;AAED;;;;;;;;GAQG;AACH,qFANW,MAAM,QACN,UAAU,eACV,IAAI,MAAM,WAAU,uBACpB,IAAI,MAAM,CAAC,qBACX,IAAI,MAAM,WAAW,QAgC/B;AAED;;;;;;;GAOG;AACH,uEAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QAgCrB;2BAtH0B,2CAA2C"}
@@ -1,18 +1,18 @@
1
1
  import { Uint32Heap } from "../../../../collection/heap/Uint32Heap.js";
2
- import { build_vertex_quadratics } from "./quadratic/build_vertex_quadratics.js";
2
+ import { max2 } from "../../../../math/max2.js";
3
3
  import {
4
4
  compute_edge_collapse_cost,
5
5
  decimate_edge_collapse_snap,
6
6
  edge_collapse_pick_target_vertex
7
7
  } from "./decimate_edge_collapse_snap.js";
8
- import { max2 } from "../../../../math/max2.js";
8
+ import { build_vertex_quadratics } from "./quadratic/build_vertex_quadratics.js";
9
9
 
10
10
  /**
11
11
  *
12
12
  * @param {Iterable<TopoEdge>} edges
13
13
  * @param {Set<number>} restricted_vertices
14
14
  * @param {Uint32Heap} heap
15
- * @param {Map<number, Quadratic3>} vertex_quadratics
15
+ * @param {Map<number, Quadric3>} vertex_quadratics
16
16
  * @param {Map<number,TopoEdge>} edge_lookup
17
17
  */
18
18
  export function edge_collapse_populate_heap(edges, restricted_vertices, heap, vertex_quadratics, edge_lookup) {
@@ -44,7 +44,7 @@ export function edge_collapse_populate_heap(edges, restricted_vertices, heap, ve
44
44
  * @param {Uint32Heap} heap
45
45
  * @param {Map<number,TopoEdge>} edge_lookup
46
46
  * @param {Set<number>} restricted_vertices
47
- * @param {Map<number, Quadratic3>} vertex_quadratics
47
+ * @param {Map<number, Quadric3>} vertex_quadratics
48
48
  */
49
49
  export function edge_collapse_reduce_face_count(mesh, num_faces_to_remove, heap, edge_lookup, restricted_vertices, vertex_quadratics) {
50
50
  const mesh_faces = mesh.getFaces();
@@ -99,7 +99,7 @@ export function simplifyTopoMesh2(mesh, num_faces_to_remove, restricted_vertices
99
99
  const heap = new Uint32Heap(mesh.getEdges().size);
100
100
  /**
101
101
  *
102
- * @type {Map<number, Quadratic3>}
102
+ * @type {Map<number, Quadric3>}
103
103
  */
104
104
  const vertex_quadratics = new Map();
105
105
 
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Compute face normals for all allocated triangular faces, overwriting any previous value.
3
+ * Non-triangular faces are left untouched.
4
+ *
5
+ * @param {BinaryTopology} mesh
6
+ */
7
+ export function bt_mesh_compute_face_normals(mesh: BinaryTopology): void;
8
+ //# sourceMappingURL=bt_mesh_compute_face_normals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_compute_face_normals.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,yEAwCC"}
@@ -0,0 +1,56 @@
1
+ import { assert } from "../../../../../../assert.js";
2
+ import { v3_compute_triangle_normal } from "../../../../triangle/v3_compute_triangle_normal.js";
3
+ import { NULL_POINTER } from "../BinaryTopology.js";
4
+
5
+ const scratch_normal = new Float32Array(3);
6
+ const scratch_coord_a = new Float32Array(3);
7
+ const scratch_coord_b = new Float32Array(3);
8
+ const scratch_coord_c = new Float32Array(3);
9
+
10
+ /**
11
+ * Compute face normals for all allocated triangular faces, overwriting any previous value.
12
+ * Non-triangular faces are left untouched.
13
+ *
14
+ * @param {BinaryTopology} mesh
15
+ */
16
+ export function bt_mesh_compute_face_normals(mesh) {
17
+ assert.defined(mesh, "mesh");
18
+ assert.notNull(mesh, "mesh");
19
+ assert.equal(mesh.isBinaryTopology, true, "mesh.isBinaryTopology !== true");
20
+
21
+ const face_pool = mesh.faces;
22
+ const face_count = face_pool.size;
23
+
24
+ for (let face_id = 0; face_id < face_count; face_id++) {
25
+ if (!face_pool.is_allocated(face_id)) {
26
+ continue;
27
+ }
28
+
29
+ const loop_a = mesh.face_read_loop(face_id);
30
+
31
+ if (loop_a === NULL_POINTER) {
32
+ continue;
33
+ }
34
+
35
+ const loop_b = mesh.loop_read_next(loop_a);
36
+ const loop_c = mesh.loop_read_next(loop_b);
37
+
38
+ // must be a triangle
39
+ if (mesh.loop_read_next(loop_c) !== loop_a) {
40
+ continue;
41
+ }
42
+
43
+ mesh.vertex_read_coordinate(scratch_coord_a, 0, mesh.loop_read_vertex(loop_a));
44
+ mesh.vertex_read_coordinate(scratch_coord_b, 0, mesh.loop_read_vertex(loop_b));
45
+ mesh.vertex_read_coordinate(scratch_coord_c, 0, mesh.loop_read_vertex(loop_c));
46
+
47
+ v3_compute_triangle_normal(
48
+ scratch_normal, 0,
49
+ scratch_coord_a[0], scratch_coord_a[1], scratch_coord_a[2],
50
+ scratch_coord_b[0], scratch_coord_b[1], scratch_coord_b[2],
51
+ scratch_coord_c[0], scratch_coord_c[1], scratch_coord_c[2]
52
+ );
53
+
54
+ mesh.face_write_normal(face_id, scratch_normal, 0);
55
+ }
56
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Compute a per-vertex quadric error metric from every allocated triangular face.
3
+ *
4
+ * For each triangular face the supporting plane (n, d) is derived from its loop
5
+ * vertices; the corresponding quadric is then accumulated onto each of those
6
+ * three vertices' Quadric3. Non-triangular faces are skipped.
7
+ *
8
+ * @param {BinaryTopology} mesh
9
+ * @returns {Quadric3[]} Array indexed by vertex ID. Entries for unallocated
10
+ * vertex slots are `undefined`.
11
+ */
12
+ export function bt_mesh_compute_vertex_quadratics(mesh: BinaryTopology): Quadric3[];
13
+ import { Quadric3 } from "../../../simplify/quadratic/Quadric3.js";
14
+ //# sourceMappingURL=bt_mesh_compute_vertex_quadratics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_compute_vertex_quadratics.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js"],"names":[],"mappings":"AAWA;;;;;;;;;;GAUG;AACH,yEAHa,QAAQ,EAAE,CA2EtB;yBA3FwB,yCAAyC"}
@@ -0,0 +1,95 @@
1
+ import { assert } from "../../../../../../assert.js";
2
+ import { v3_dot } from "../../../../../vec3/v3_dot.js";
3
+ import { v3_compute_triangle_normal } from "../../../../triangle/v3_compute_triangle_normal.js";
4
+ import { Quadric3 } from "../../../simplify/quadratic/Quadric3.js";
5
+ import { NULL_POINTER } from "../BinaryTopology.js";
6
+
7
+ const scratch_coord_a = new Float32Array(3);
8
+ const scratch_coord_b = new Float32Array(3);
9
+ const scratch_coord_c = new Float32Array(3);
10
+ const scratch_normal = new Float32Array(3);
11
+
12
+ /**
13
+ * Compute a per-vertex quadric error metric from every allocated triangular face.
14
+ *
15
+ * For each triangular face the supporting plane (n, d) is derived from its loop
16
+ * vertices; the corresponding quadric is then accumulated onto each of those
17
+ * three vertices' Quadric3. Non-triangular faces are skipped.
18
+ *
19
+ * @param {BinaryTopology} mesh
20
+ * @returns {Quadric3[]} Array indexed by vertex ID. Entries for unallocated
21
+ * vertex slots are `undefined`.
22
+ */
23
+ export function bt_mesh_compute_vertex_quadratics(mesh) {
24
+ assert.defined(mesh, 'mesh');
25
+ assert.notNull(mesh, 'mesh');
26
+
27
+ const vertex_pool = mesh.vertices;
28
+ const vertex_count = vertex_pool.size;
29
+
30
+ const quadratics = new Array(vertex_count);
31
+
32
+ for (let v = 0; v < vertex_count; v++) {
33
+ if (vertex_pool.is_allocated(v)) {
34
+ quadratics[v] = new Quadric3();
35
+ }
36
+ }
37
+
38
+ const face_pool = mesh.faces;
39
+ const face_count = face_pool.size;
40
+
41
+ const q = new Quadric3();
42
+
43
+ for (let f = 0; f < face_count; f++) {
44
+ if (!face_pool.is_allocated(f)) {
45
+ continue;
46
+ }
47
+
48
+ const la = mesh.face_read_loop(f);
49
+
50
+ if (la === NULL_POINTER) {
51
+ continue;
52
+ }
53
+
54
+ const lb = mesh.loop_read_next(la);
55
+ const lc = mesh.loop_read_next(lb);
56
+
57
+ // Skip non-triangles
58
+ if (mesh.loop_read_next(lc) !== la) {
59
+ continue;
60
+ }
61
+
62
+ const va = mesh.loop_read_vertex(la);
63
+ const vb = mesh.loop_read_vertex(lb);
64
+ const vc = mesh.loop_read_vertex(lc);
65
+
66
+ mesh.vertex_read_coordinate(scratch_coord_a, 0, va);
67
+ mesh.vertex_read_coordinate(scratch_coord_b, 0, vb);
68
+ mesh.vertex_read_coordinate(scratch_coord_c, 0, vc);
69
+
70
+ v3_compute_triangle_normal(
71
+ scratch_normal, 0,
72
+ scratch_coord_a[0], scratch_coord_a[1], scratch_coord_a[2],
73
+ scratch_coord_b[0], scratch_coord_b[1], scratch_coord_b[2],
74
+ scratch_coord_c[0], scratch_coord_c[1], scratch_coord_c[2]
75
+ );
76
+
77
+ const nx = scratch_normal[0];
78
+ const ny = scratch_normal[1];
79
+ const nz = scratch_normal[2];
80
+
81
+ // Plane offset: d = -(n . v_a)
82
+ const d = -v3_dot(
83
+ nx, ny, nz,
84
+ scratch_coord_a[0], scratch_coord_a[1], scratch_coord_a[2]
85
+ );
86
+
87
+ q.setFromVector4(nx, ny, nz, d);
88
+
89
+ quadratics[va].add(q);
90
+ quadratics[vb].add(q);
91
+ quadratics[vc].add(q);
92
+ }
93
+
94
+ return quadratics;
95
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Simplify a mesh in-place by greedily collapsing the cheapest edges (per a quadric
3
+ * error metric) until the allocated face count drops to `target_face_count`.
4
+ *
5
+ * Vertices listed in `restricted_vertices` are pinned: they are neither moved nor
6
+ * removed. If the target cannot be reached (e.g. too many restricted vertices),
7
+ * simplification stops early without failing.
8
+ *
9
+ * At the end of the call the mesh is cleaned up so that no dangling loops, edges,
10
+ * or vertices remain in the topology.
11
+ *
12
+ * @param {BinaryTopology} mesh Mesh to simplify in-place.
13
+ * @param {number} target_face_count Desired face count after simplification.
14
+ * @param {Set<number>} [restricted_vertices] Vertices that must not move or be removed.
15
+ */
16
+ export function bt_mesh_simplify(mesh: BinaryTopology, target_face_count: number, restricted_vertices?: Set<number>): void;
17
+ //# sourceMappingURL=bt_mesh_simplify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_simplify.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js"],"names":[],"mappings":"AAqLA;;;;;;;;;;;;;;GAcG;AACH,0EAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QA2HrB"}
@@ -0,0 +1,352 @@
1
+ import { assert } from "../../../../../../assert.js";
2
+ import { Uint32Heap } from "../../../../../../collection/heap/Uint32Heap.js";
3
+ import { Quadric3 } from "../../../simplify/quadratic/Quadric3.js";
4
+ import { NULL_POINTER } from "../BinaryTopology.js";
5
+ import { bt_loop_kill } from "./bt_loop_kill.js";
6
+ import { bt_mesh_compute_vertex_quadratics } from "./bt_mesh_compute_vertex_quadratics.js";
7
+ import { bt_edge_collapse } from "./edge/bt_edge_collapse.js";
8
+ import { bt_edge_kill } from "./edge/bt_edge_kill.js";
9
+ import { bt_edge_kill_parallels } from "./edge/bt_edge_kill_parallels.js";
10
+ import { bt_vert_fuse_duplicate_edges } from "./vertex/bt_vert_fuse_duplicate_edges.js";
11
+ import { bt_vert_kill } from "./vertex/bt_vert_kill.js";
12
+
13
+ // Module-level scratch buffers to keep GC pressure low across calls.
14
+ const scratch_coord_a = new Float32Array(3);
15
+ const scratch_coord_b = new Float32Array(3);
16
+ const scratch_target = new Float32Array(3);
17
+ const scratch_combined_q = new Quadric3();
18
+
19
+ /**
20
+ * Count the number of still-live faces attached to an edge via its radial loop cycle.
21
+ * Orphan loops (whose face was already killed by a prior collapse) are skipped so
22
+ * that callers tracking running face totals don't double-decrement.
23
+ *
24
+ * @param {BinaryTopology} mesh
25
+ * @param {number} edge_id
26
+ * @returns {number}
27
+ */
28
+ function count_edge_faces(mesh, edge_id) {
29
+ const l0 = mesh.edge_read_loop(edge_id);
30
+
31
+ if (l0 === NULL_POINTER) {
32
+ return 0;
33
+ }
34
+
35
+ const face_pool = mesh.faces;
36
+
37
+ let count = 0;
38
+ let l = l0;
39
+
40
+ do {
41
+ const f = mesh.loop_read_face(l);
42
+
43
+ if (f !== NULL_POINTER && face_pool.is_allocated(f)) {
44
+ count++;
45
+ }
46
+
47
+ l = mesh.loop_read_radial_next(l);
48
+ } while (l !== l0 && l !== NULL_POINTER);
49
+
50
+ return count;
51
+ }
52
+
53
+ /**
54
+ * Swap the v1/v2 slots of an edge. Disk cycle pointers for the two slots are swapped
55
+ * alongside so that the disk cycles on both attached vertices remain valid.
56
+ *
57
+ * @param {BinaryTopology} mesh
58
+ * @param {number} edge_id
59
+ */
60
+ function swap_edge_vertex_slots(mesh, edge_id) {
61
+ const v1 = mesh.edge_read_vertex1(edge_id);
62
+ const v2 = mesh.edge_read_vertex2(edge_id);
63
+
64
+ const v1_next = mesh.edge_read_v1_disk_next(edge_id);
65
+ const v1_prev = mesh.edge_read_v1_disk_prev(edge_id);
66
+ const v2_next = mesh.edge_read_v2_disk_next(edge_id);
67
+ const v2_prev = mesh.edge_read_v2_disk_prev(edge_id);
68
+
69
+ mesh.edge_write_vertex1(edge_id, v2);
70
+ mesh.edge_write_vertex2(edge_id, v1);
71
+
72
+ mesh.edge_write_v1_disk_next(edge_id, v2_next);
73
+ mesh.edge_write_v1_disk_prev(edge_id, v2_prev);
74
+ mesh.edge_write_v2_disk_next(edge_id, v1_next);
75
+ mesh.edge_write_v2_disk_prev(edge_id, v1_prev);
76
+ }
77
+
78
+ /**
79
+ * Evaluate a potential edge collapse: choose the optimal surviving vertex position
80
+ * and return the quadric-based cost. Returns Infinity if the edge cannot be collapsed.
81
+ *
82
+ * @param {BinaryTopology} mesh
83
+ * @param {number} edge_id
84
+ * @param {Quadric3[]} quadratics
85
+ * @param {Set<number>} restricted_vertices
86
+ * @param {Float32Array|number[]} out_target Target position is written here.
87
+ * @returns {number}
88
+ */
89
+ function evaluate_edge_collapse(mesh, edge_id, quadratics, restricted_vertices, out_target) {
90
+ const v1 = mesh.edge_read_vertex1(edge_id);
91
+ const v2 = mesh.edge_read_vertex2(edge_id);
92
+
93
+ if (v1 === v2) {
94
+ // Degenerate self-loop, not collapsible
95
+ return Number.POSITIVE_INFINITY;
96
+ }
97
+
98
+ const v1_restricted = restricted_vertices.has(v1);
99
+ const v2_restricted = restricted_vertices.has(v2);
100
+
101
+ if (v1_restricted && v2_restricted) {
102
+ return Number.POSITIVE_INFINITY;
103
+ }
104
+
105
+ const q1 = quadratics[v1];
106
+ const q2 = quadratics[v2];
107
+
108
+ // Missing quadrics mean at least one endpoint was a prior victim that somehow
109
+ // persisted as a topology endpoint (e.g. via a self-loop produced from parallel
110
+ // edges). Such edges are not useful collapse candidates.
111
+ if (q1 === null || q2 === null || q1 === undefined || q2 === undefined) {
112
+ return Number.POSITIVE_INFINITY;
113
+ }
114
+
115
+ if (v1_restricted) {
116
+ mesh.vertex_read_coordinate(out_target, 0, v1);
117
+ } else if (v2_restricted) {
118
+ mesh.vertex_read_coordinate(out_target, 0, v2);
119
+ } else {
120
+ scratch_combined_q.copy(q1);
121
+ scratch_combined_q.add(q2);
122
+
123
+ if (!scratch_combined_q.optimize(out_target)) {
124
+ // Singular combined tensor: fall back to the midpoint
125
+ mesh.vertex_read_coordinate(scratch_coord_a, 0, v1);
126
+ mesh.vertex_read_coordinate(scratch_coord_b, 0, v2);
127
+
128
+ out_target[0] = (scratch_coord_a[0] + scratch_coord_b[0]) * 0.5;
129
+ out_target[1] = (scratch_coord_a[1] + scratch_coord_b[1]) * 0.5;
130
+ out_target[2] = (scratch_coord_a[2] + scratch_coord_b[2]) * 0.5;
131
+ }
132
+ }
133
+
134
+ const tx = out_target[0];
135
+ const ty = out_target[1];
136
+ const tz = out_target[2];
137
+
138
+ // Cost can drift slightly negative for near-zero values due to fp noise.
139
+ return Math.abs(q1.evaluate(tx, ty, tz) + q2.evaluate(tx, ty, tz));
140
+ }
141
+
142
+ /**
143
+ * Re-score every edge in the disk cycle of `survivor` that is currently in the heap.
144
+ * Edges that were never inserted (e.g. both endpoints restricted) are left untouched.
145
+ *
146
+ * @param {BinaryTopology} mesh
147
+ * @param {number} survivor
148
+ * @param {Quadric3[]} quadratics
149
+ * @param {Set<number>} restricted_vertices
150
+ * @param {Uint32Heap} heap
151
+ */
152
+ function update_neighbor_costs(mesh, survivor, quadratics, restricted_vertices, heap) {
153
+ let curr_e = mesh.vertex_read_edge(survivor);
154
+
155
+ if (curr_e === NULL_POINTER) {
156
+ return;
157
+ }
158
+
159
+ const start_e = curr_e;
160
+
161
+ do {
162
+ // Grab the next edge in the disk cycle before we do anything that might
163
+ // make `curr_e` harder to reason about (the cycle itself is not mutated).
164
+ let next_e;
165
+ if (mesh.edge_read_vertex1(curr_e) === survivor) {
166
+ next_e = mesh.edge_read_v1_disk_next(curr_e);
167
+ } else {
168
+ next_e = mesh.edge_read_v2_disk_next(curr_e);
169
+ }
170
+
171
+ const idx = heap.find_index_by_id(curr_e);
172
+
173
+ if (idx !== -1) {
174
+ const cost = evaluate_edge_collapse(mesh, curr_e, quadratics, restricted_vertices, scratch_target);
175
+ heap.__update_score_by_index(idx, cost);
176
+ }
177
+
178
+ curr_e = next_e;
179
+ } while (curr_e !== start_e && curr_e !== NULL_POINTER);
180
+ }
181
+
182
+ /**
183
+ * Simplify a mesh in-place by greedily collapsing the cheapest edges (per a quadric
184
+ * error metric) until the allocated face count drops to `target_face_count`.
185
+ *
186
+ * Vertices listed in `restricted_vertices` are pinned: they are neither moved nor
187
+ * removed. If the target cannot be reached (e.g. too many restricted vertices),
188
+ * simplification stops early without failing.
189
+ *
190
+ * At the end of the call the mesh is cleaned up so that no dangling loops, edges,
191
+ * or vertices remain in the topology.
192
+ *
193
+ * @param {BinaryTopology} mesh Mesh to simplify in-place.
194
+ * @param {number} target_face_count Desired face count after simplification.
195
+ * @param {Set<number>} [restricted_vertices] Vertices that must not move or be removed.
196
+ */
197
+ export function bt_mesh_simplify(
198
+ mesh,
199
+ target_face_count,
200
+ restricted_vertices = new Set()
201
+ ) {
202
+ assert.defined(mesh, 'mesh');
203
+ assert.notNull(mesh, 'mesh');
204
+ assert.equal(mesh.isBinaryTopology, true, 'mesh.isBinaryTopology !== true');
205
+ assert.isNonNegativeInteger(target_face_count, 'target_face_count');
206
+ assert.defined(restricted_vertices, 'restricted_vertices');
207
+ assert.notNull(restricted_vertices, 'restricted_vertices');
208
+
209
+ const face_pool = mesh.faces;
210
+
211
+ // Count allocated faces up front so we can track progress without scanning each iteration.
212
+ let current_face_count = 0;
213
+ for (let f = 0; f < face_pool.size; f++) {
214
+ if (face_pool.is_allocated(f)) {
215
+ current_face_count++;
216
+ }
217
+ }
218
+
219
+ if (current_face_count <= target_face_count) {
220
+ return;
221
+ }
222
+
223
+ const quadratics = bt_mesh_compute_vertex_quadratics(mesh);
224
+
225
+ const edge_pool = mesh.edges;
226
+ const heap = new Uint32Heap(edge_pool.size);
227
+
228
+ for (let e = 0; e < edge_pool.size; e++) {
229
+ if (!edge_pool.is_allocated(e)) {
230
+ continue;
231
+ }
232
+
233
+ const cost = evaluate_edge_collapse(mesh, e, quadratics, restricted_vertices, scratch_target);
234
+
235
+ if (cost === Number.POSITIVE_INFINITY) {
236
+ continue;
237
+ }
238
+
239
+ heap.insert(e, cost);
240
+ }
241
+
242
+ // Safety bound: every real collapse removes at least one face; the extra headroom
243
+ // accommodates no-op collapses on orphan edges that may appear after a collapse
244
+ // produces duplicate parallel edges.
245
+ const max_iterations = current_face_count * 10 + heap.size + 10;
246
+ let iterations = 0;
247
+
248
+ while (
249
+ !heap.is_empty()
250
+ && current_face_count > target_face_count
251
+ && iterations < max_iterations
252
+ ) {
253
+ iterations++;
254
+
255
+ const edge_id = heap.pop_min();
256
+
257
+ if (!edge_pool.is_allocated(edge_id)) {
258
+ // Edge was killed by an earlier collapse (e.g. adjacent face death).
259
+ continue;
260
+ }
261
+
262
+ const v1 = mesh.edge_read_vertex1(edge_id);
263
+ const v2 = mesh.edge_read_vertex2(edge_id);
264
+
265
+ if (v1 === v2) {
266
+ // Degenerate self-loop produced by a prior collapse of parallel edges.
267
+ continue;
268
+ }
269
+
270
+ const v1_restricted = restricted_vertices.has(v1);
271
+ const v2_restricted = restricted_vertices.has(v2);
272
+
273
+ if (v1_restricted && v2_restricted) {
274
+ continue;
275
+ }
276
+
277
+ const cost = evaluate_edge_collapse(mesh, edge_id, quadratics, restricted_vertices, scratch_target);
278
+
279
+ if (cost === Number.POSITIVE_INFINITY) {
280
+ continue;
281
+ }
282
+
283
+ const face_deaths = count_edge_faces(mesh, edge_id);
284
+
285
+ // bt_edge_collapse keeps v1 as the survivor. If v2 is the pinned endpoint,
286
+ // swap the edge's slots so the pinned vertex ends up in the v1 position.
287
+ if (v2_restricted) {
288
+ swap_edge_vertex_slots(mesh, edge_id);
289
+ }
290
+
291
+ const pre_survivor = mesh.edge_read_vertex1(edge_id);
292
+ const pre_victim = mesh.edge_read_vertex2(edge_id);
293
+
294
+ // Accumulate victim's quadric onto the survivor before the topology mutates.
295
+ quadratics[pre_survivor].add(quadratics[pre_victim]);
296
+ quadratics[pre_victim] = null;
297
+
298
+ // Kill any direct parallel edges between survivor and victim. Leaving them in
299
+ // place would cause bt_vertex_replace (inside bt_edge_collapse) to produce a
300
+ // self-loop edge (surv, surv), which corrupts the survivor's disk cycle.
301
+ current_face_count -= bt_edge_kill_parallels(mesh, edge_id);
302
+
303
+ const survivor = bt_edge_collapse(mesh, edge_id, scratch_target, 0);
304
+
305
+ current_face_count -= face_deaths;
306
+
307
+ // Link-vertex parallels produced by this collapse will become direct parallels
308
+ // at the next collapse that touches them, so fuse them now while disk cycles
309
+ // are still intact.
310
+ current_face_count -= bt_vert_fuse_duplicate_edges(mesh, survivor);
311
+
312
+ update_neighbor_costs(mesh, survivor, quadratics, restricted_vertices, heap);
313
+ }
314
+
315
+ // Drop any loose loops/edges/verts produced during collapses, preserving
316
+ // restricted vertices even if they ended up isolated.
317
+ cleanup_faceless_preserving_restricted(mesh, restricted_vertices);
318
+ }
319
+
320
+ /**
321
+ * Variant of bt_mesh_cleanup_faceless_references that never releases a restricted
322
+ * vertex even when it has no remaining edges.
323
+ *
324
+ * @param {BinaryTopology} mesh
325
+ * @param {Set<number>} restricted_vertices
326
+ */
327
+ function cleanup_faceless_preserving_restricted(mesh, restricted_vertices) {
328
+ const loop_pool = mesh.loops;
329
+ for (let l = 0; l < loop_pool.size; l++) {
330
+ if (!loop_pool.is_allocated(l)) continue;
331
+ if (mesh.loop_read_face(l) === NULL_POINTER) {
332
+ bt_loop_kill(mesh, l);
333
+ }
334
+ }
335
+
336
+ const edge_pool = mesh.edges;
337
+ for (let e = 0; e < edge_pool.size; e++) {
338
+ if (!edge_pool.is_allocated(e)) continue;
339
+ if (mesh.edge_read_loop(e) === NULL_POINTER) {
340
+ bt_edge_kill(mesh, e);
341
+ }
342
+ }
343
+
344
+ const vertex_pool = mesh.vertices;
345
+ for (let v = 0; v < vertex_pool.size; v++) {
346
+ if (!vertex_pool.is_allocated(v)) continue;
347
+ if (restricted_vertices.has(v)) continue;
348
+ if (mesh.vertex_read_edge(v) === NULL_POINTER) {
349
+ bt_vert_kill(mesh, v);
350
+ }
351
+ }
352
+ }