@woosh/meep-engine 2.133.5 → 2.134.1
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/build/bundle-worker-image-decoder.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts +20 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +50 -0
- package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.d.ts +4 -4
- package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/EdgeCollapseCandidate.js +2 -2
- package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.d.ts +2 -2
- package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/build_edge_collapse_candidates.js +1 -1
- package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.d.ts +2 -2
- package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/computeEdgeCollapseCost.js +1 -1
- package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts +4 -4
- package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +3 -3
- package/src/core/geom/3d/topology/simplify/quadratic/{Quadratic3.d.ts → Quadric3.d.ts} +55 -29
- package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.d.ts.map +1 -0
- package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.js +307 -0
- package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.d.ts +7 -7
- package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.js +39 -13
- package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.d.ts +2 -2
- package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.js +1 -1
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.d.ts +2 -2
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh.js +2 -2
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.d.ts +4 -4
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.d.ts.map +1 -1
- package/src/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +5 -5
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts +8 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.d.ts +14 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +95 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +17 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +352 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.d.ts +21 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts +16 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +90 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -0
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +148 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.js +4 -4
- package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.d.ts +15 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +48 -0
- package/src/core/graph/coloring/colorizeGraphMCS.js +1 -1
- package/src/core/graph/convert_graph_to_dot_string.d.ts.map +1 -1
- package/src/core/graph/convert_graph_to_dot_string.js +2 -1
- package/src/core/graph/graph_compute_distance_matrix.d.ts +18 -1
- package/src/core/graph/graph_compute_distance_matrix.d.ts.map +1 -1
- package/src/core/graph/graph_compute_distance_matrix.js +21 -2
- package/src/core/graph/graph_compute_laplacian_matrix.d.ts.map +1 -1
- package/src/core/graph/graph_compute_laplacian_matrix.js +2 -1
- package/src/core/graph/graph_k_means_cluster.d.ts +10 -3
- package/src/core/graph/graph_k_means_cluster.d.ts.map +1 -1
- package/src/core/graph/graph_k_means_cluster.js +250 -123
- package/src/core/graph/layout/CircleLayout.js +6 -6
- package/src/core/graph/layout/Connection.js +1 -1
- package/src/core/graph/layout/box/aabb2_force_into_container.d.ts.map +1 -1
- package/src/core/graph/layout/box/aabb2_force_into_container.js +4 -2
- package/src/core/math/statistics/computeStatisticalMedian.js +1 -1
- package/src/engine/navigation/mesh/NavigationMesh.d.ts +6 -4
- package/src/engine/navigation/mesh/NavigationMesh.d.ts.map +1 -1
- package/src/engine/navigation/mesh/NavigationMesh.js +212 -190
- package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/navmesh_build_topology.js +20 -7
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts +15 -0
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts.map +1 -0
- package/src/engine/navigation/mesh/bvh_query_nearest_face.js +131 -0
- package/src/core/geom/3d/topology/simplify/quadratic/Quadratic3.d.ts.map +0 -1
- 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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 {
|
|
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 {
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
+
}
|