@woosh/meep-engine 2.48.20 → 2.48.22
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/package.json +1 -1
- package/src/{engine/graphics/micron/build/clustering → core/geom/3d/topology/util}/compute_face_connection_weight.js +40 -35
- package/src/core/graph/build_face_graph_from_mesh.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +0 -3
- package/src/engine/graphics/micron/MICRON_GEOMETRY_FIELD.js +0 -1
- package/src/engine/graphics/micron/MICRON_URI_FIELD.js +0 -1
- package/src/engine/graphics/micron/build/MICRON_PATCH_SIZE_MAX.js +0 -5
- package/src/engine/graphics/micron/build/PatchRepresentation.js +0 -424
- package/src/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +0 -311
- package/src/engine/graphics/micron/build/build_geometry_info.js +0 -21
- package/src/engine/graphics/micron/build/clustering/assignInitialPatchNeighbours.js +0 -63
- package/src/engine/graphics/micron/build/clustering/assign_patch_neighbours_from_topology.js +0 -46
- package/src/engine/graphics/micron/build/clustering/build_clustering_2.js +0 -37
- package/src/engine/graphics/micron/build/clustering/build_leaf_patches.js +0 -326
- package/src/engine/graphics/micron/build/clustering/build_leaf_patches_metis.js +0 -76
- package/src/engine/graphics/micron/build/clustering/computeBorderLengthChange.js +0 -59
- package/src/engine/graphics/micron/build/clustering/computeFaceCurvatureScore.js +0 -57
- package/src/engine/graphics/micron/build/clustering/computeOpenFaceFreedom.js +0 -35
- package/src/engine/graphics/micron/build/clustering/populateOpenFaceNeighboursForPatch.js +0 -56
- package/src/engine/graphics/micron/build/clustering/validate_leaf_patch_connectivity.js +0 -44
- package/src/engine/graphics/micron/build/compute_micron_buffer_array_constructor.js +0 -24
- package/src/engine/graphics/micron/build/compute_vertex_remap_cost.js +0 -29
- package/src/engine/graphics/micron/build/debug/build_clustering_info.js +0 -58
- package/src/engine/graphics/micron/build/debug/build_graph_info.js +0 -59
- package/src/engine/graphics/micron/build/fill_patch_geometry_data.js +0 -270
- package/src/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +0 -196
- package/src/engine/graphics/micron/build/hierarchy/build_intermediate_patch_topology.js +0 -162
- package/src/engine/graphics/micron/build/hierarchy/build_merge_graph.js +0 -89
- package/src/engine/graphics/micron/build/hierarchy/computePatchMergeScore.js +0 -146
- package/src/engine/graphics/micron/build/hierarchy/compute_patches_shared_vertex_count.js +0 -32
- package/src/engine/graphics/micron/build/hierarchy/compute_patches_shared_vertices.js +0 -34
- package/src/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -581
- package/src/engine/graphics/micron/build/hierarchy/metis_cluster_clusters.js +0 -53
- package/src/engine/graphics/micron/build/hierarchy/optimize_graph_partitioning_balance.js +0 -515
- package/src/engine/graphics/micron/build/hierarchy/patch_combine.js +0 -180
- package/src/engine/graphics/micron/build/hierarchy/patch_fill_holes_by_uncollapse.js +0 -60
- package/src/engine/graphics/micron/build/hierarchy/patch_stitch_parent_border.js +0 -320
- package/src/engine/graphics/micron/build/hierarchy/qvdr_build_simplified_clusters.js +0 -547
- package/src/engine/graphics/micron/build/hierarchy/qvdr_build_tree.js +0 -140
- package/src/engine/graphics/micron/buildPatchwork.js +0 -68
- package/src/engine/graphics/micron/convert_three_object_to_micron.js +0 -179
- package/src/engine/graphics/micron/debug/VirtualGeometryStats.js +0 -42
- package/src/engine/graphics/micron/format/MICRON_GEOMETRY_PROPERTY_NAME.js +0 -1
- package/src/engine/graphics/micron/format/MicronGeometry.d.ts +0 -21
- package/src/engine/graphics/micron/format/MicronGeometry.js +0 -334
- package/src/engine/graphics/micron/format/MicronGeometryPatch.d.ts +0 -3
- package/src/engine/graphics/micron/format/MicronGeometryPatch.js +0 -205
- package/src/engine/graphics/micron/format/MicronGeometryPatchOccurance.js +0 -50
- package/src/engine/graphics/micron/format/ThreeMicronMesh.d.ts +0 -10
- package/src/engine/graphics/micron/format/ThreeMicronMesh.js +0 -45
- package/src/engine/graphics/micron/format/VirtualGeometry.js +0 -158
- package/src/engine/graphics/micron/format/micron_build_proxy_geometry.js +0 -205
- package/src/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.js +0 -123
- package/src/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.spec.js +0 -63
- package/src/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js +0 -83
- package/src/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.spec.js +0 -51
- package/src/engine/graphics/micron/format/serialization/deserialize_attribute_spec.js +0 -25
- package/src/engine/graphics/micron/format/serialization/deserialize_patch.js +0 -106
- package/src/engine/graphics/micron/format/serialization/serialize_attribute_spec.js +0 -18
- package/src/engine/graphics/micron/format/serialization/serialize_patch.js +0 -84
- package/src/engine/graphics/micron/format/validate_patch_bounds.js +0 -69
- package/src/engine/graphics/micron/plugin/GLTFAssetTransformer.js +0 -265
- package/src/engine/graphics/micron/plugin/GLTF_MICRON_ID_FIELD.js +0 -5
- package/src/engine/graphics/micron/plugin/MicronRenderPlugin.d.ts +0 -8
- package/src/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -150
- package/src/engine/graphics/micron/plugin/serialization/BufferGeometrySerializationAdapter.js +0 -176
- package/src/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +0 -216
- package/src/engine/graphics/micron/prototypeMicronProxyBuild.js +0 -227
- package/src/engine/graphics/micron/prototypeVirtualGeometry.js +0 -911
- package/src/engine/graphics/micron/render/PatchCacheKey.js +0 -79
- package/src/engine/graphics/micron/render/VIRTUAL_MESH_FLAG.d.ts +0 -1
- package/src/engine/graphics/micron/render/VIRTUAL_MESH_FLAG.js +0 -1
- package/src/engine/graphics/micron/render/VirtualGeometryBuilder.js +0 -207
- package/src/engine/graphics/micron/render/approximateTriangleArea.js +0 -25
- package/src/engine/graphics/micron/render/culling/PatchCullingSystem.js +0 -129
- package/src/engine/graphics/micron/render/instanced/PatchDataTextures.js +0 -329
- package/src/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +0 -175
- package/src/engine/graphics/micron/render/instanced/shader/constants.js +0 -3
- package/src/engine/graphics/micron/render/instanced/shader/gen_micron_vertex_attribute_texture_name.js +0 -8
- package/src/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +0 -250
- package/src/engine/graphics/micron/render/makeThreeMeshFromVirtualGeometry.js +0 -37
- package/src/engine/graphics/micron/render/refinement/ActivePatchFlags.js +0 -8
- package/src/engine/graphics/micron/render/refinement/ActivePatchList.js +0 -241
- package/src/engine/graphics/micron/render/refinement/ActivePatchRecord.js +0 -154
- package/src/engine/graphics/micron/render/refinement/RefinementSpec.js +0 -84
- package/src/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +0 -133
- package/src/engine/graphics/micron/render/refinement/is_patch_facing_back.js +0 -43
- package/src/engine/graphics/micron/render/refinement/is_patch_visible.js +0 -37
- package/src/engine/graphics/micron/render/v1/MaterialContext.js +0 -150
- package/src/engine/graphics/micron/render/v1/MaterialVertexSpec.js +0 -115
- package/src/engine/graphics/micron/render/v1/MicronRenderContext.js +0 -145
- package/src/engine/graphics/micron/render/v1/ThreeVirtualGeometryAdapter.js +0 -255
- package/src/engine/graphics/micron/render/v1/VGThreeRenderer.js +0 -142
- package/src/engine/graphics/micron/render/v1/getTransformedPositionsCached.js +0 -54
- package/src/engine/graphics/micron/simplifyGeometry.js +0 -26
- package/src/engine/graphics/micron/util/patchToBufferGeometry.js +0 -19
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
import { graph_k_means_cluster_detailed } from "../../../../../core/graph/graph_k_means_cluster.js";
|
|
2
|
-
import { array_remove_first } from "../../../../../core/collection/array/array_remove_first.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* @param {number[]} G
|
|
7
|
-
* @param {number[]} T
|
|
8
|
-
* @param {number[]} C
|
|
9
|
-
* @param {number} k
|
|
10
|
-
* @param {number[]} node_cluster_assignment
|
|
11
|
-
* @param {number[][]} parts
|
|
12
|
-
* @returns {number}
|
|
13
|
-
*/
|
|
14
|
-
function process_cycle(
|
|
15
|
-
G,
|
|
16
|
-
T,
|
|
17
|
-
C,
|
|
18
|
-
k,
|
|
19
|
-
node_cluster_assignment,
|
|
20
|
-
parts
|
|
21
|
-
) {
|
|
22
|
-
let ops = 0;
|
|
23
|
-
let j = 0;
|
|
24
|
-
for (let i = 0; i < k; i++, j = i) {
|
|
25
|
-
if (C[i] !== 255 || T[j] < 0) {
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
while (C[j] === 255 && T[j] >= 0) {
|
|
30
|
-
C[j] = i;
|
|
31
|
-
j = T[j];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (C[j] === i && T[j] >= 0) {
|
|
35
|
-
do {
|
|
36
|
-
const node_index = G[j];
|
|
37
|
-
const destination_cluster_index = T[j];
|
|
38
|
-
|
|
39
|
-
const source_cluster_index = node_cluster_assignment[node_index];
|
|
40
|
-
|
|
41
|
-
if (source_cluster_index !== destination_cluster_index) {
|
|
42
|
-
|
|
43
|
-
// rewrite assignment
|
|
44
|
-
node_cluster_assignment[node_index] = destination_cluster_index;
|
|
45
|
-
|
|
46
|
-
// move between part arrays
|
|
47
|
-
array_remove_first(parts[source_cluster_index], node_index);
|
|
48
|
-
parts[destination_cluster_index].push(node_index);
|
|
49
|
-
|
|
50
|
-
ops++;
|
|
51
|
-
} else {
|
|
52
|
-
// no movement
|
|
53
|
-
// break;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
j = destination_cluster_index;
|
|
57
|
-
} while (C[j] === i);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return ops;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const move_suggestion = {
|
|
65
|
-
t: 0,
|
|
66
|
-
g: 0
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Algorithm GreedyCompute for a part to compute which node to go and where
|
|
71
|
-
* @param {number} part_index
|
|
72
|
-
* @param {number[][]} parts part
|
|
73
|
-
* @param {number} k number of partitions
|
|
74
|
-
* @param {Graph<T>} graph
|
|
75
|
-
* @param {T[]} node_array
|
|
76
|
-
* @param {number[]} node_cluster_assignment
|
|
77
|
-
* @param {Float32Array} pressure_vector
|
|
78
|
-
* @param {Uint8Array} visited_vector
|
|
79
|
-
* @param {Map<T, number>} node_index_map
|
|
80
|
-
*/
|
|
81
|
-
function greedy_compute(
|
|
82
|
-
part_index,
|
|
83
|
-
parts,
|
|
84
|
-
k,
|
|
85
|
-
graph,
|
|
86
|
-
node_array,
|
|
87
|
-
node_cluster_assignment,
|
|
88
|
-
node_index_map,
|
|
89
|
-
pressure_vector,
|
|
90
|
-
visited_vector
|
|
91
|
-
) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let best_node_pressure = 1;
|
|
95
|
-
let best_overall_gain = Number.NEGATIVE_INFINITY
|
|
96
|
-
|
|
97
|
-
let i, j;
|
|
98
|
-
|
|
99
|
-
const part = parts[part_index];
|
|
100
|
-
|
|
101
|
-
const part_size = part.length;
|
|
102
|
-
|
|
103
|
-
for (i = 0; i < k; i++) {
|
|
104
|
-
|
|
105
|
-
if (i === part_index) {
|
|
106
|
-
// skip self
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const cluster_size = parts[i].length;
|
|
111
|
-
|
|
112
|
-
pressure_vector[i] = part_size / (cluster_size + 0.01);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
for (i = 0; i < part_size; i++) {
|
|
116
|
-
// reset gain vector
|
|
117
|
-
visited_vector.fill(0);
|
|
118
|
-
|
|
119
|
-
const node_index = part[i];
|
|
120
|
-
|
|
121
|
-
// de-reference actual node
|
|
122
|
-
const node = node_array[node_index];
|
|
123
|
-
|
|
124
|
-
// get neighbours of node
|
|
125
|
-
const node_container = graph.getNodeContainer(node);
|
|
126
|
-
const edges = node_container.getEdges();
|
|
127
|
-
|
|
128
|
-
// lets consider where this node might move
|
|
129
|
-
const edge_count = edges.length;
|
|
130
|
-
|
|
131
|
-
for (let i1 = 0; i1 < edge_count; i1++) {
|
|
132
|
-
const edge = edges[i1];
|
|
133
|
-
|
|
134
|
-
const neighbour = edge.other(node);
|
|
135
|
-
|
|
136
|
-
const neighbour_index = node_index_map.get(neighbour);
|
|
137
|
-
|
|
138
|
-
const neighbour_cluster_index = node_cluster_assignment[neighbour_index];
|
|
139
|
-
|
|
140
|
-
visited_vector[neighbour_cluster_index] = 1;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// find X such that Gv[x] is highest value
|
|
144
|
-
|
|
145
|
-
for (j = 0; j < k; j++) {
|
|
146
|
-
|
|
147
|
-
if (j === part_index) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (visited_vector[j] === 0) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const pressure = pressure_vector[j];
|
|
156
|
-
|
|
157
|
-
if (pressure < best_node_pressure) {
|
|
158
|
-
continue;
|
|
159
|
-
} else {
|
|
160
|
-
best_node_pressure = pressure;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const gain = compute_node_move_cost(
|
|
164
|
-
graph,
|
|
165
|
-
node_container,
|
|
166
|
-
part_index,
|
|
167
|
-
j,
|
|
168
|
-
node_array,
|
|
169
|
-
node_cluster_assignment,
|
|
170
|
-
node_index_map
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
if (gain > best_overall_gain) {
|
|
174
|
-
// update gain
|
|
175
|
-
move_suggestion.g = node_index;
|
|
176
|
-
move_suggestion.t = j;
|
|
177
|
-
|
|
178
|
-
best_overall_gain = gain;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return best_overall_gain;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
*
|
|
189
|
-
* @param {number} node_index
|
|
190
|
-
* @param {number} destination_cluster_index
|
|
191
|
-
* @param {number} source_cluster_index
|
|
192
|
-
* @param {number[]} node_cluster_assignment
|
|
193
|
-
* @param {number[][]} parts
|
|
194
|
-
*/
|
|
195
|
-
function pass_node(
|
|
196
|
-
node_index,
|
|
197
|
-
destination_cluster_index,
|
|
198
|
-
source_cluster_index,
|
|
199
|
-
node_cluster_assignment,
|
|
200
|
-
parts
|
|
201
|
-
) {
|
|
202
|
-
|
|
203
|
-
// rewrite assignment
|
|
204
|
-
node_cluster_assignment[node_index] = destination_cluster_index;
|
|
205
|
-
|
|
206
|
-
// move between part arrays
|
|
207
|
-
array_remove_first(parts[source_cluster_index], node_index);
|
|
208
|
-
parts[destination_cluster_index].push(node_index);
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* @template T
|
|
214
|
-
* @param {number} k
|
|
215
|
-
* @param {number[][]} parts
|
|
216
|
-
* @param {Graph<T>} graph
|
|
217
|
-
* @param {T[]} node_array
|
|
218
|
-
* @param {number[]} node_cluster_assignment
|
|
219
|
-
* @param {Map<T, number>} node_index_map
|
|
220
|
-
*/
|
|
221
|
-
function greedy_cycle(
|
|
222
|
-
k,
|
|
223
|
-
parts,
|
|
224
|
-
graph,
|
|
225
|
-
node_array,
|
|
226
|
-
node_cluster_assignment,
|
|
227
|
-
node_index_map
|
|
228
|
-
) {
|
|
229
|
-
if (k > 127) {
|
|
230
|
-
throw new Error('Too many parts, algorithm only supports 127');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const n = node_array.length;
|
|
234
|
-
|
|
235
|
-
const pressure_vector = new Float32Array(k);
|
|
236
|
-
const visited_vector = new Uint8Array(k);
|
|
237
|
-
|
|
238
|
-
const balance_lower_limit = Math.floor(n / k);
|
|
239
|
-
const balance_upper_limit = Math.ceil(n / k);
|
|
240
|
-
|
|
241
|
-
let step_op_count, i, balanced;
|
|
242
|
-
|
|
243
|
-
move_suggestion.g = -1;
|
|
244
|
-
move_suggestion.t = -1;
|
|
245
|
-
|
|
246
|
-
do {
|
|
247
|
-
balanced = true;
|
|
248
|
-
|
|
249
|
-
for (i = 0; i < k; i++) {
|
|
250
|
-
const cluster = parts[i];
|
|
251
|
-
const cluster_size = cluster.length;
|
|
252
|
-
if (cluster_size < balance_lower_limit || cluster_size > balance_upper_limit) {
|
|
253
|
-
balanced = false;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (balanced) {
|
|
258
|
-
// already balanced, stop here
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
step_op_count = 0;
|
|
263
|
-
|
|
264
|
-
for (i = 0; i < k; i++) {
|
|
265
|
-
|
|
266
|
-
greedy_compute(
|
|
267
|
-
i,
|
|
268
|
-
parts,
|
|
269
|
-
k,
|
|
270
|
-
graph,
|
|
271
|
-
node_array,
|
|
272
|
-
node_cluster_assignment,
|
|
273
|
-
node_index_map,
|
|
274
|
-
pressure_vector,
|
|
275
|
-
visited_vector
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const target_cluster = move_suggestion.t;
|
|
279
|
-
|
|
280
|
-
if (target_cluster === -1) {
|
|
281
|
-
// no target
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const node_index = move_suggestion.g;
|
|
286
|
-
|
|
287
|
-
pass_node(node_index, target_cluster, i, node_cluster_assignment, parts);
|
|
288
|
-
|
|
289
|
-
move_suggestion.g = -1;
|
|
290
|
-
move_suggestion.t = -1;
|
|
291
|
-
|
|
292
|
-
step_op_count++;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
while (step_op_count > 0);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* compute total weight of edges that will be fully within the target cluster
|
|
301
|
-
* @template T
|
|
302
|
-
* @param {Graph<T,WeightedEdge<T>>} graph
|
|
303
|
-
* @param {NodeContainer<T>} node_container
|
|
304
|
-
* @param {number} source_cluster
|
|
305
|
-
* @param {number} target_cluster
|
|
306
|
-
* @param {T[]} node_array
|
|
307
|
-
* @param {number[]} node_cluster_assignments Node to cluster references
|
|
308
|
-
* @returns {number} lower is better
|
|
309
|
-
* @param {Map<T, number>} node_index_map
|
|
310
|
-
*/
|
|
311
|
-
function compute_node_move_cost(
|
|
312
|
-
graph,
|
|
313
|
-
node_container,
|
|
314
|
-
source_cluster,
|
|
315
|
-
target_cluster,
|
|
316
|
-
node_array,
|
|
317
|
-
node_cluster_assignments,
|
|
318
|
-
node_index_map
|
|
319
|
-
) {
|
|
320
|
-
let weight_gain = 0;
|
|
321
|
-
|
|
322
|
-
const existing_edges = node_container.getEdges();
|
|
323
|
-
|
|
324
|
-
const node = node_container.node;
|
|
325
|
-
|
|
326
|
-
// find edges that make up the current cut
|
|
327
|
-
const existing_edge_count = existing_edges.length;
|
|
328
|
-
|
|
329
|
-
for (let i = 0; i < existing_edge_count; i++) {
|
|
330
|
-
const edge = existing_edges[i];
|
|
331
|
-
|
|
332
|
-
const node_neighbour = edge.other(node);
|
|
333
|
-
|
|
334
|
-
const neighbour_index = node_index_map.get(node_neighbour);
|
|
335
|
-
|
|
336
|
-
const neighbour_cluster_index = node_cluster_assignments[neighbour_index];
|
|
337
|
-
|
|
338
|
-
if (neighbour_cluster_index === target_cluster) {
|
|
339
|
-
// this edge will be consumed by the new cluster and will be removed from current cut
|
|
340
|
-
weight_gain += edge.weight;
|
|
341
|
-
} else if (neighbour_cluster_index === source_cluster) {
|
|
342
|
-
weight_gain -= edge.weight;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return weight_gain;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Based on GreedyCycle algorithm
|
|
351
|
-
* @see Fast Approximate Graph Partitioning Algorithms
|
|
352
|
-
* @template T
|
|
353
|
-
* @param {Graph<T>} graph
|
|
354
|
-
* @param {T[][]} parts
|
|
355
|
-
* @param {T[]} node_array
|
|
356
|
-
* @param {number[]} node_cluster_assignments Node to cluster references
|
|
357
|
-
*/
|
|
358
|
-
function optimize_graph_partitioning_balance(
|
|
359
|
-
graph,
|
|
360
|
-
parts,
|
|
361
|
-
node_array,
|
|
362
|
-
node_cluster_assignments
|
|
363
|
-
) {
|
|
364
|
-
let optimization_op_count_step = 0;
|
|
365
|
-
|
|
366
|
-
const node_count = node_array.length;
|
|
367
|
-
|
|
368
|
-
const partition_count = parts.length;
|
|
369
|
-
|
|
370
|
-
const upper_cluster_size_bound = Math.ceil(node_count / partition_count);
|
|
371
|
-
const lower_cluster_size_bound = Math.floor(node_count / partition_count);
|
|
372
|
-
|
|
373
|
-
// quick check if partitions are already optimal
|
|
374
|
-
let optimal_cluster_sizes = true;
|
|
375
|
-
for (let i = 0; i < partition_count; i++) {
|
|
376
|
-
const part = parts[i];
|
|
377
|
-
|
|
378
|
-
const cluster_size = part.length;
|
|
379
|
-
if (cluster_size < lower_cluster_size_bound || cluster_size > upper_cluster_size_bound) {
|
|
380
|
-
optimal_cluster_sizes = false;
|
|
381
|
-
break;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (optimal_cluster_sizes) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// compute current cut weight
|
|
390
|
-
|
|
391
|
-
do {
|
|
392
|
-
optimization_op_count_step = 0;
|
|
393
|
-
|
|
394
|
-
let best_source_cluster = -1;
|
|
395
|
-
let best_cost = 0;
|
|
396
|
-
let best_target_node = -1;
|
|
397
|
-
let best_target_node_origin_cluster = -1;
|
|
398
|
-
|
|
399
|
-
// TODO we can compute multiple moves in a single pass
|
|
400
|
-
|
|
401
|
-
for (let i = 0; i < node_count; i++) {
|
|
402
|
-
const node = node_array[i];
|
|
403
|
-
|
|
404
|
-
const node_partition_index = node_cluster_assignments[i];
|
|
405
|
-
|
|
406
|
-
const source_part = parts[node_partition_index];
|
|
407
|
-
const source_part_size = source_part.length;
|
|
408
|
-
if (source_part_size >= upper_cluster_size_bound) {
|
|
409
|
-
// partition is already full, skip
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const container = graph.getNodeContainer(node);
|
|
414
|
-
|
|
415
|
-
const edges = container.getEdges();
|
|
416
|
-
|
|
417
|
-
// lets consider where this node might move
|
|
418
|
-
const edge_count = edges.length;
|
|
419
|
-
for (let i1 = 0; i1 < edge_count; i1++) {
|
|
420
|
-
const edge = edges[i1];
|
|
421
|
-
|
|
422
|
-
const neighbour = edge.other(node);
|
|
423
|
-
|
|
424
|
-
const neighbour_index = node_array.indexOf(neighbour);
|
|
425
|
-
|
|
426
|
-
const neighbour_partition_index = node_cluster_assignments[neighbour_index];
|
|
427
|
-
|
|
428
|
-
if (neighbour_partition_index === node_partition_index) {
|
|
429
|
-
// belongs to the same cluster, no swap/move opportunity
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// if (parts[neighbour_partition_index].length <= upper_cluster_size_bound) {
|
|
434
|
-
// neighbour partition would have too few elements
|
|
435
|
-
// continue;
|
|
436
|
-
// }
|
|
437
|
-
|
|
438
|
-
const cost = compute_node_move_cost(graph, neighbour, neighbour_partition_index, node_partition_index, node_array, node_cluster_assignments);
|
|
439
|
-
|
|
440
|
-
if (cost < best_cost) {
|
|
441
|
-
|
|
442
|
-
best_source_cluster = node_partition_index;
|
|
443
|
-
best_target_node = neighbour_index;
|
|
444
|
-
best_cost = cost;
|
|
445
|
-
best_target_node_origin_cluster = neighbour_partition_index;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (best_source_cluster !== -1) {
|
|
453
|
-
// do move
|
|
454
|
-
node_cluster_assignments[best_target_node] = best_source_cluster;
|
|
455
|
-
|
|
456
|
-
array_remove_first(parts[best_target_node_origin_cluster], best_target_node);
|
|
457
|
-
parts[best_source_cluster].push(best_target_node);
|
|
458
|
-
|
|
459
|
-
optimization_op_count_step++;
|
|
460
|
-
} else {
|
|
461
|
-
for (let i = 0; i < parts.length; i++) {
|
|
462
|
-
if (parts[i].length > upper_cluster_size_bound) {
|
|
463
|
-
|
|
464
|
-
debugger;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
} while (optimization_op_count_step > 0);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* @template T
|
|
474
|
-
* @param {T[]} node_array
|
|
475
|
-
* @param {number} k
|
|
476
|
-
* @param {Graph<T>} graph
|
|
477
|
-
* @returns {number[][]}
|
|
478
|
-
*/
|
|
479
|
-
export function optimize_graph_partitioning_balance_detailed(node_array, k, graph) {
|
|
480
|
-
const node_count = node_array.length;
|
|
481
|
-
/**
|
|
482
|
-
*
|
|
483
|
-
* @type {number[]}
|
|
484
|
-
*/
|
|
485
|
-
const node_cluster_assignments = new Uint8Array(node_count);
|
|
486
|
-
// fill with "UNASSIGNED" value
|
|
487
|
-
node_cluster_assignments.fill(255);
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* build node index
|
|
491
|
-
* @type {Map<T, number>}
|
|
492
|
-
*/
|
|
493
|
-
const node_index_map = new Map();
|
|
494
|
-
|
|
495
|
-
for (let i = 0; i < node_count; i++) {
|
|
496
|
-
node_index_map.set(node_array[i], i);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const clusters = graph_k_means_cluster_detailed(node_array, k, 1249, node_cluster_assignments, node_index_map, graph);
|
|
500
|
-
|
|
501
|
-
greedy_cycle(k, clusters, graph, node_array, node_cluster_assignments, node_index_map);
|
|
502
|
-
|
|
503
|
-
return clusters;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* @template T
|
|
508
|
-
* @param {Graph<T>} graph
|
|
509
|
-
* @param {number} k
|
|
510
|
-
* @returns {number[][]}
|
|
511
|
-
*/
|
|
512
|
-
export function graph_build_balanced_partitions(graph, k) {
|
|
513
|
-
const node_array = Array.from(graph.getNodes());
|
|
514
|
-
return optimize_graph_partitioning_balance_detailed(node_array, k, graph);
|
|
515
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { PatchRepresentation } from "../PatchRepresentation.js";
|
|
2
|
-
import { array_copy } from "../../../../../core/collection/array/copyArray.js";
|
|
3
|
-
import { array_copy_unique } from "../../../../../core/collection/array/array_copy_unique.js";
|
|
4
|
-
import { array_remove_first } from "../../../../../core/collection/array/array_remove_first.js";
|
|
5
|
-
import { max2 } from "../../../../../core/math/max2.js";
|
|
6
|
-
import { array_push_if_unique } from "../../../../../core/collection/array/array_push_if_unique.js";
|
|
7
|
-
import {
|
|
8
|
-
compute_bounding_sphere_of_2_spheres
|
|
9
|
-
} from "../../../../../core/geom/3d/compute_bounding_sphere_of_2_spheres.js";
|
|
10
|
-
import { compute_bounding_cone_of_2_cones } from "../../../../../core/geom/3d/cone/compute_bounding_cone_of_2_cones.js";
|
|
11
|
-
import { ConicRay } from "../../../../../core/geom/ConicRay.js";
|
|
12
|
-
import { passThrough } from "../../../../../core/function/Functions.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param {PatchRepresentation} r
|
|
17
|
-
*/
|
|
18
|
-
export function compute_neighbours_from_children(r) {
|
|
19
|
-
|
|
20
|
-
const this_neighbours = r.neighbours;
|
|
21
|
-
|
|
22
|
-
const children = r.children;
|
|
23
|
-
|
|
24
|
-
const child_count = children.length;
|
|
25
|
-
|
|
26
|
-
const child_0 = children[0];
|
|
27
|
-
|
|
28
|
-
array_copy(child_0.neighbours, 0, this_neighbours, 0, child_0.neighbours.length);
|
|
29
|
-
for (let i = 1; i < child_count; i++) {
|
|
30
|
-
const child = children[i];
|
|
31
|
-
array_copy_unique(child.neighbours, 0, this_neighbours, this_neighbours.length, child.neighbours.length)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// remove children from own neighbourhood set
|
|
35
|
-
for (let i = 0; i < child_count; i++) {
|
|
36
|
-
const child = children[i];
|
|
37
|
-
array_remove_first(this_neighbours, child);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// add self to neighbours
|
|
41
|
-
let this_neighbour_count = this_neighbours.length;
|
|
42
|
-
for (let i = 0; i < this_neighbour_count; i++) {
|
|
43
|
-
const neighbour = this_neighbours[i];
|
|
44
|
-
|
|
45
|
-
// remove neighbours that are part of the hierarchy
|
|
46
|
-
if (neighbour.isDescendantOf(r)) {
|
|
47
|
-
|
|
48
|
-
this_neighbours.splice(i, 1);
|
|
49
|
-
|
|
50
|
-
i--;
|
|
51
|
-
this_neighbour_count--;
|
|
52
|
-
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// link back from neighbour to this new patch
|
|
57
|
-
array_push_if_unique(neighbour.neighbours, r);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @template T
|
|
65
|
-
* @param {ConicRay} result
|
|
66
|
-
* @param {T[]} inputs
|
|
67
|
-
* @param {function(T):ConicRay} extract
|
|
68
|
-
*/
|
|
69
|
-
function compute_bounding_cone_of_cones(result, inputs, extract = passThrough) {
|
|
70
|
-
const open = inputs.map(extract);
|
|
71
|
-
|
|
72
|
-
while (open.length > 1) {
|
|
73
|
-
const r0 = open.shift();
|
|
74
|
-
const r1 = open.shift();
|
|
75
|
-
|
|
76
|
-
const out = new ConicRay();
|
|
77
|
-
|
|
78
|
-
compute_bounding_cone_of_2_cones(out, r0, r1);
|
|
79
|
-
|
|
80
|
-
open.push(out);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
result.copy(open[0]);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @template T
|
|
88
|
-
* @param {number[]|vec4} result
|
|
89
|
-
* @param {T[]} inputs
|
|
90
|
-
* @param {function(T):(number[]|vec4)} extract
|
|
91
|
-
*/
|
|
92
|
-
function compute_bounding_sphere_of_spheres(result, inputs, extract) {
|
|
93
|
-
const open = inputs.map(extract);
|
|
94
|
-
|
|
95
|
-
while (open.length > 1) {
|
|
96
|
-
const r0 = open.shift();
|
|
97
|
-
const r1 = open.shift();
|
|
98
|
-
|
|
99
|
-
const out = [];
|
|
100
|
-
|
|
101
|
-
compute_bounding_sphere_of_2_spheres(out, r0, r1);
|
|
102
|
-
|
|
103
|
-
open.push(out);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
array_copy(open[0], 0, result, 0, 4);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
*
|
|
111
|
-
* @param {PatchRepresentation[]} children
|
|
112
|
-
* @returns {PatchRepresentation}
|
|
113
|
-
*/
|
|
114
|
-
export function patch_build_parent(children) {
|
|
115
|
-
|
|
116
|
-
const r = new PatchRepresentation();
|
|
117
|
-
|
|
118
|
-
r.children = children;
|
|
119
|
-
|
|
120
|
-
for (let i = 0; i < children.length; i++) {
|
|
121
|
-
const child = children[i];
|
|
122
|
-
// set parent
|
|
123
|
-
child.parents.push(r);
|
|
124
|
-
|
|
125
|
-
//figure out lod
|
|
126
|
-
r.lod = max2(r.lod, child.lod + 1);
|
|
127
|
-
|
|
128
|
-
// combine bounds
|
|
129
|
-
// r.bounding_box.expandToFit(child.bounding_box);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// combine bounds
|
|
133
|
-
// compute_bounding_cone_of_cones(r.normal_cone, children, c => c.normal_cone);
|
|
134
|
-
// compute_bounding_sphere_of_spheres(r.bounding_sphere, children, t => t.bounding_sphere);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return r;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
*
|
|
142
|
-
* @return {PatchRepresentation}
|
|
143
|
-
* @param {PatchRepresentation} a
|
|
144
|
-
* @param {PatchRepresentation} b
|
|
145
|
-
* @param {boolean} is_connected are patches neighbours?
|
|
146
|
-
*/
|
|
147
|
-
export function patch_combine(a, b, is_connected) {
|
|
148
|
-
|
|
149
|
-
const r = new PatchRepresentation();
|
|
150
|
-
|
|
151
|
-
//figure out lod
|
|
152
|
-
r.lod = max2(a.lod, b.lod) + 1;
|
|
153
|
-
|
|
154
|
-
r.children = [a, b];
|
|
155
|
-
|
|
156
|
-
// set parent
|
|
157
|
-
a.parent = r;
|
|
158
|
-
b.parent = r;
|
|
159
|
-
|
|
160
|
-
// patch neighbours
|
|
161
|
-
compute_neighbours_from_children(r);
|
|
162
|
-
|
|
163
|
-
//combine bounds
|
|
164
|
-
r.bounding_box.expandToFit(a.bounding_box);
|
|
165
|
-
r.bounding_box.expandToFit(b.bounding_box);
|
|
166
|
-
|
|
167
|
-
compute_bounding_cone_of_2_cones(r.normal_cone, a.normal_cone, b.normal_cone);
|
|
168
|
-
compute_bounding_sphere_of_2_spheres(r.bounding_sphere, a.bounding_sphere, b.bounding_sphere);
|
|
169
|
-
|
|
170
|
-
// const errors = [];
|
|
171
|
-
// const is_valid = r.validate(errors.push, errors);
|
|
172
|
-
//
|
|
173
|
-
// if (!is_valid) {
|
|
174
|
-
// debugger;
|
|
175
|
-
// }
|
|
176
|
-
|
|
177
|
-
// build_intermediate_patch_topology(r);
|
|
178
|
-
|
|
179
|
-
return r;
|
|
180
|
-
}
|