@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.
Files changed (83) 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/core/graph/coloring/colorizeGraphMCS.js +1 -1
  59. package/src/core/graph/convert_graph_to_dot_string.d.ts.map +1 -1
  60. package/src/core/graph/convert_graph_to_dot_string.js +2 -1
  61. package/src/core/graph/graph_compute_distance_matrix.d.ts +18 -1
  62. package/src/core/graph/graph_compute_distance_matrix.d.ts.map +1 -1
  63. package/src/core/graph/graph_compute_distance_matrix.js +21 -2
  64. package/src/core/graph/graph_compute_laplacian_matrix.d.ts.map +1 -1
  65. package/src/core/graph/graph_compute_laplacian_matrix.js +2 -1
  66. package/src/core/graph/graph_k_means_cluster.d.ts +10 -3
  67. package/src/core/graph/graph_k_means_cluster.d.ts.map +1 -1
  68. package/src/core/graph/graph_k_means_cluster.js +250 -123
  69. package/src/core/graph/layout/CircleLayout.js +6 -6
  70. package/src/core/graph/layout/Connection.js +1 -1
  71. package/src/core/graph/layout/box/aabb2_force_into_container.d.ts.map +1 -1
  72. package/src/core/graph/layout/box/aabb2_force_into_container.js +4 -2
  73. package/src/core/math/statistics/computeStatisticalMedian.js +1 -1
  74. package/src/engine/navigation/mesh/NavigationMesh.d.ts +6 -4
  75. package/src/engine/navigation/mesh/NavigationMesh.d.ts.map +1 -1
  76. package/src/engine/navigation/mesh/NavigationMesh.js +212 -190
  77. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  78. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +20 -7
  79. package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts +15 -0
  80. package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts.map +1 -0
  81. package/src/engine/navigation/mesh/bvh_query_nearest_face.js +131 -0
  82. package/src/core/geom/3d/topology/simplify/quadratic/Quadratic3.d.ts.map +0 -1
  83. package/src/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +0 -302
@@ -12,7 +12,8 @@ import { graph_compute_degree_matrix } from "./graph_compute_degree_matrix.js";
12
12
  * @param {Map<T,number>} node_indices Map from graph nodes to matrix indices.
13
13
  */
14
14
  export function graph_compute_laplacian_matrix(graph, result, node_indices) {
15
- const degree = new SquareMatrix(result.size, BinaryDataType.Uint8);
15
+ // Uint32 prevents silent overflow for high-degree or multi-edge graphs
16
+ const degree = new SquareMatrix(result.size, BinaryDataType.Uint32);
16
17
  const adjacency = new SquareMatrix(result.size, BinaryDataType.Uint8);
17
18
 
18
19
  graph_compute_degree_matrix(graph, degree, node_indices);
@@ -2,14 +2,21 @@
2
2
  * @param {MultiNode<T>[]} node_array
3
3
  * @param {number} k
4
4
  * @param {number} random_seed
5
- * @param {number[]|Uint8Array|Uint16Array|Uint32Array} node_cluster_assignments
5
+ * @param {number[]|Uint32Array} node_cluster_assignments pre-filled with UNASSIGNED_CLUSTER
6
6
  * @param {Map<T, number>} node_index_map
7
7
  * @param {Graph<MultiNode<T>>} graph
8
8
  * @returns {number[][]}
9
9
  */
10
- export function graph_k_means_cluster_detailed(node_array: MultiNode<T>[], k: number, random_seed: number, node_cluster_assignments: number[] | Uint8Array | Uint16Array | Uint32Array, node_index_map: Map<T, number>, graph: Graph<MultiNode<T>>): number[][];
10
+ export function graph_k_means_cluster_detailed(node_array: MultiNode<T>[], k: number, random_seed: number, node_cluster_assignments: number[] | Uint32Array, node_index_map: Map<T, number>, graph: Graph<MultiNode<T>>): number[][];
11
11
  /**
12
- * Partition graph into K parts using K-means algorithm
12
+ * Partition graph into K parts using K-medoids (graph K-means).
13
+ * Iteratively reassigns nodes to the nearest medoid, then recomputes medoids
14
+ * as the within-cluster node minimising the sum of distances to other members.
15
+ * Stops when medoids stabilise or MAX_ITERATIONS is reached.
16
+ *
17
+ * Distances are BFS-based and so treat all edges as unit-weight; see
18
+ * {@link graph_compute_distance_matrix}.
19
+ *
13
20
  * @template T
14
21
  * @param {Graph<T>} graph
15
22
  * @param {number} k number of desired parts
@@ -1 +1 @@
1
- {"version":3,"file":"graph_k_means_cluster.d.ts","sourceRoot":"","sources":["../../../../src/core/graph/graph_k_means_cluster.js"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,2DARW,cAAc,KACd,MAAM,eACN,MAAM,4BACN,MAAM,EAAE,GAAC,UAAU,GAAC,WAAW,GAAC,WAAW,kBAC3C,OAAO,MAAM,CAAC,+BAEZ,MAAM,EAAE,EAAE,CA2EtB;AAED;;;;;;;GAOG;AACH,6DAJW,MAAM,eACN,MAAM,GACJ,MAAM,EAAE,EAAE,CA0BtB"}
1
+ {"version":3,"file":"graph_k_means_cluster.d.ts","sourceRoot":"","sources":["../../../../src/core/graph/graph_k_means_cluster.js"],"names":[],"mappings":"AA0DA;;;;;;;;GAQG;AACH,2DARW,cAAc,KACd,MAAM,eACN,MAAM,4BACN,MAAM,EAAE,GAAC,WAAW,kBACpB,OAAO,MAAM,CAAC,+BAEZ,MAAM,EAAE,EAAE,CA+ItB;AAED;;;;;;;;;;;;;;GAcG;AACH,6DAJW,MAAM,eACN,MAAM,GACJ,MAAM,EAAE,EAAE,CA0BtB"}
@@ -1,123 +1,250 @@
1
- //
2
- import { randomIntegerBetween } from "../math/random/randomIntegerBetween.js";
3
- import { seededRandom } from "../math/random/seededRandom.js";
4
- import { graph_compute_distance_matrix } from "./graph_compute_distance_matrix.js";
5
-
6
-
7
- /**
8
- * @param {MultiNode<T>[]} node_array
9
- * @param {number} k
10
- * @param {number} random_seed
11
- * @param {number[]|Uint8Array|Uint16Array|Uint32Array} node_cluster_assignments
12
- * @param {Map<T, number>} node_index_map
13
- * @param {Graph<MultiNode<T>>} graph
14
- * @returns {number[][]}
15
- */
16
- export function graph_k_means_cluster_detailed(
17
- node_array,
18
- k,
19
- random_seed,
20
- node_cluster_assignments,
21
- node_index_map,
22
- graph
23
- ) {
24
- const total_node_count = node_array.length;
25
-
26
- if (k > total_node_count) {
27
- throw new Error(`Not enough nodes in the graph, K(=${k}) > |V| (=${total_node_count})`);
28
- }
29
-
30
- const random = seededRandom(random_seed);
31
-
32
- //seeds
33
- const clusters_seeds = [];
34
-
35
- /**
36
- *
37
- * @type {number[][]}
38
- */
39
- const clusters = [];
40
-
41
- const UNASSIGNED_CLUSTER = 255;
42
-
43
- // seed clusters
44
- for (let i = 0; i < k; i++) {
45
- //
46
- const node_index = randomIntegerBetween(random, 0, total_node_count);
47
-
48
- if (node_cluster_assignments[node_index] !== UNASSIGNED_CLUSTER) {
49
- // node taken, retry
50
- i--;
51
- continue;
52
- }
53
-
54
- node_cluster_assignments[node_index] = i;
55
-
56
- clusters_seeds[i] = node_index;
57
- clusters[i] = [node_index];
58
- }
59
-
60
- const m_distances = graph_compute_distance_matrix(graph, node_array, clusters_seeds, node_index_map);
61
-
62
- for (let node_index = 0; node_index < total_node_count; node_index++) {
63
-
64
- if (node_cluster_assignments[node_index] !== UNASSIGNED_CLUSTER) {
65
- // already assigned
66
- continue;
67
- }
68
-
69
- let closest_cluster = 0;
70
- let closest_distance = Number.POSITIVE_INFINITY;
71
-
72
- for (let cluster_index = 0; cluster_index < k; cluster_index++) {
73
- const cluster_seed = clusters_seeds[cluster_index];
74
-
75
- const distance = m_distances.getCellValue(cluster_seed, node_index);
76
-
77
- if (distance < closest_distance) {
78
- closest_distance = distance;
79
- closest_cluster = cluster_index;
80
- }
81
- }
82
-
83
- // assign node to closest cluster
84
- node_cluster_assignments[node_index] = closest_cluster;
85
- clusters[closest_cluster].push(node_index);
86
- }
87
-
88
- return clusters;
89
- }
90
-
91
- /**
92
- * Partition graph into K parts using K-means algorithm
93
- * @template T
94
- * @param {Graph<T>} graph
95
- * @param {number} k number of desired parts
96
- * @param {number} random_seed seed for random number generator, useful for restarting partitioning
97
- * @returns {number[][]}
98
- */
99
- export function graph_k_means_cluster(graph, k, random_seed) {
100
-
101
- const node_array = Array.from(graph.getNodes());
102
- const node_count = node_array.length;
103
-
104
- /**
105
- *
106
- * @type {Uint8Array}
107
- */
108
- const node_cluster_assignments = new Uint8Array(node_count);
109
- // fill with "UNASSIGNED" value
110
- node_cluster_assignments.fill(255);
111
-
112
- /**
113
- * build node index
114
- * @type {Map<T, number>}
115
- */
116
- const node_index_map = new Map();
117
-
118
- for (let i = 0; i < node_count; i++) {
119
- node_index_map.set(node_array[i], i);
120
- }
121
-
122
- return graph_k_means_cluster_detailed(node_array, k, random_seed, node_cluster_assignments, node_index_map, graph);
123
- }
1
+ import { assert } from "../assert.js";
2
+ import { randomIntegerBetween } from "../math/random/randomIntegerBetween.js";
3
+ import { seededRandom } from "../math/random/seededRandom.js";
4
+ import { graph_compute_distance_matrix } from "./graph_compute_distance_matrix.js";
5
+
6
+
7
+ /**
8
+ * Sentinel value marking a node as not yet assigned to any cluster.
9
+ * Must be distinguishable from any valid cluster index 0..k-1, so k must
10
+ * stay strictly below this.
11
+ * @type {number}
12
+ */
13
+ const UNASSIGNED_CLUSTER = 0xFFFFFFFF;
14
+
15
+ /**
16
+ * Maximum number of refinement iterations before the algorithm stops even
17
+ * if medoids haven't stabilised. In practice convergence is reached within
18
+ * a handful of iterations on well-connected graphs.
19
+ * @type {number}
20
+ */
21
+ const MAX_ITERATIONS = 32;
22
+
23
+
24
+ /**
25
+ * Find the medoid of a set of nodes: the member with minimum sum of
26
+ * distances to every other member.
27
+ *
28
+ * @param {number[]} members cluster member node indices
29
+ * @param {SquareMatrix} m_distances matrix whose columns for each member index are filled
30
+ * @returns {number} node index of the medoid
31
+ */
32
+ function find_cluster_medoid(members, m_distances) {
33
+ const n = members.length;
34
+
35
+ let best_member = members[0];
36
+ let best_sum = Number.POSITIVE_INFINITY;
37
+
38
+ for (let i = 0; i < n; i++) {
39
+ const candidate = members[i];
40
+
41
+ let sum = 0;
42
+ for (let j = 0; j < n; j++) {
43
+ if (i === j) {
44
+ continue;
45
+ }
46
+ sum += m_distances.getCellValue(candidate, members[j]);
47
+ }
48
+
49
+ if (sum < best_sum) {
50
+ best_sum = sum;
51
+ best_member = candidate;
52
+ }
53
+ }
54
+
55
+ return best_member;
56
+ }
57
+
58
+
59
+ /**
60
+ * @param {MultiNode<T>[]} node_array
61
+ * @param {number} k
62
+ * @param {number} random_seed
63
+ * @param {number[]|Uint32Array} node_cluster_assignments pre-filled with UNASSIGNED_CLUSTER
64
+ * @param {Map<T, number>} node_index_map
65
+ * @param {Graph<MultiNode<T>>} graph
66
+ * @returns {number[][]}
67
+ */
68
+ export function graph_k_means_cluster_detailed(
69
+ node_array,
70
+ k,
71
+ random_seed,
72
+ node_cluster_assignments,
73
+ node_index_map,
74
+ graph
75
+ ) {
76
+ const total_node_count = node_array.length;
77
+
78
+ if (k > total_node_count) {
79
+ throw new Error(`Not enough nodes in the graph, K(=${k}) > |V| (=${total_node_count})`);
80
+ }
81
+
82
+ // Cluster indices are stored alongside UNASSIGNED_CLUSTER in the same array, so
83
+ // k must fit strictly below the sentinel to avoid collisions.
84
+ assert.lessThan(k, UNASSIGNED_CLUSTER, `k must be less than UNASSIGNED_CLUSTER(=${UNASSIGNED_CLUSTER})`);
85
+
86
+ if (k === 0) {
87
+ return [];
88
+ }
89
+
90
+ const random = seededRandom(random_seed);
91
+
92
+ /**
93
+ * Current medoid (centroid) node index for each cluster
94
+ * @type {number[]}
95
+ */
96
+ const seeds = [];
97
+
98
+ // Farthest-first seeding: pick the first seed uniformly at random, then each
99
+ // subsequent seed is the node whose minimum distance to any existing seed is
100
+ // largest. This reliably spreads seeds across disconnected components and
101
+ // avoids the common K-means failure mode where random initialisation puts
102
+ // every seed into the same component.
103
+ {
104
+ // randomIntegerBetween is inclusive on both ends, so the max index is count-1
105
+ const first_seed = randomIntegerBetween(random, 0, total_node_count - 1);
106
+ seeds[0] = first_seed;
107
+ node_cluster_assignments[first_seed] = 0;
108
+ }
109
+
110
+ for (let i = 1; i < k; i++) {
111
+ const m_partial = graph_compute_distance_matrix(graph, node_array, seeds, node_index_map);
112
+
113
+ let best_node = -1;
114
+ let best_min_dist = -1;
115
+
116
+ for (let node_index = 0; node_index < total_node_count; node_index++) {
117
+ if (node_cluster_assignments[node_index] !== UNASSIGNED_CLUSTER) {
118
+ // already a seed
119
+ continue;
120
+ }
121
+
122
+ let min_dist = Number.POSITIVE_INFINITY;
123
+ for (let j = 0; j < i; j++) {
124
+ const d = m_partial.getCellValue(node_index, seeds[j]);
125
+ if (d < min_dist) {
126
+ min_dist = d;
127
+ }
128
+ }
129
+
130
+ if (min_dist > best_min_dist) {
131
+ best_min_dist = min_dist;
132
+ best_node = node_index;
133
+ }
134
+ }
135
+
136
+ seeds[i] = best_node;
137
+ node_cluster_assignments[best_node] = i;
138
+ }
139
+
140
+ /**
141
+ * Node indices belonging to each cluster. Rebuilt every iteration.
142
+ * @type {number[][]}
143
+ */
144
+ let cluster_members = [];
145
+
146
+ for (let iter = 0; iter < MAX_ITERATIONS; iter++) {
147
+ // 1. Compute distances from every current seed to every node (BFS from each seed)
148
+ const m_distances_from_seeds = graph_compute_distance_matrix(graph, node_array, seeds, node_index_map);
149
+
150
+ // 2. Assign every node to its nearest seed
151
+ for (let node_index = 0; node_index < total_node_count; node_index++) {
152
+
153
+ let closest_cluster = 0;
154
+ let closest_distance = Number.POSITIVE_INFINITY;
155
+
156
+ for (let cluster_index = 0; cluster_index < k; cluster_index++) {
157
+ const cluster_seed = seeds[cluster_index];
158
+
159
+ // Matrix columns are the seed (target) indices, rows are source nodes.
160
+ // We need the distance from node_index to the seed, so read m[node_index, seed].
161
+ const distance = m_distances_from_seeds.getCellValue(node_index, cluster_seed);
162
+
163
+ if (distance < closest_distance) {
164
+ closest_distance = distance;
165
+ closest_cluster = cluster_index;
166
+ }
167
+ }
168
+
169
+ node_cluster_assignments[node_index] = closest_cluster;
170
+ }
171
+
172
+ // 3. Rebuild cluster member lists
173
+ cluster_members = [];
174
+ for (let i = 0; i < k; i++) {
175
+ cluster_members[i] = [];
176
+ }
177
+ for (let node_index = 0; node_index < total_node_count; node_index++) {
178
+ cluster_members[node_cluster_assignments[node_index]].push(node_index);
179
+ }
180
+
181
+ // 4. Recompute each seed as the medoid of its cluster
182
+ let seeds_changed = false;
183
+
184
+ for (let cluster_index = 0; cluster_index < k; cluster_index++) {
185
+ const members = cluster_members[cluster_index];
186
+
187
+ if (members.length <= 1) {
188
+ // 0 or 1 member: medoid is trivially the current seed
189
+ continue;
190
+ }
191
+
192
+ // BFS from every member to compute within-cluster pairwise distances
193
+ const m_within = graph_compute_distance_matrix(graph, node_array, members, node_index_map);
194
+
195
+ const new_seed = find_cluster_medoid(members, m_within);
196
+
197
+ if (new_seed !== seeds[cluster_index]) {
198
+ seeds[cluster_index] = new_seed;
199
+ seeds_changed = true;
200
+ }
201
+ }
202
+
203
+ if (!seeds_changed) {
204
+ break;
205
+ }
206
+ }
207
+
208
+ return cluster_members;
209
+ }
210
+
211
+ /**
212
+ * Partition graph into K parts using K-medoids (graph K-means).
213
+ * Iteratively reassigns nodes to the nearest medoid, then recomputes medoids
214
+ * as the within-cluster node minimising the sum of distances to other members.
215
+ * Stops when medoids stabilise or MAX_ITERATIONS is reached.
216
+ *
217
+ * Distances are BFS-based and so treat all edges as unit-weight; see
218
+ * {@link graph_compute_distance_matrix}.
219
+ *
220
+ * @template T
221
+ * @param {Graph<T>} graph
222
+ * @param {number} k number of desired parts
223
+ * @param {number} random_seed seed for random number generator, useful for restarting partitioning
224
+ * @returns {number[][]}
225
+ */
226
+ export function graph_k_means_cluster(graph, k, random_seed) {
227
+
228
+ const node_array = Array.from(graph.getNodes());
229
+ const node_count = node_array.length;
230
+
231
+ /**
232
+ * Uint32 so cluster indices and the UNASSIGNED_CLUSTER sentinel (0xFFFFFFFF) don't collide
233
+ * for any practical k
234
+ * @type {Uint32Array}
235
+ */
236
+ const node_cluster_assignments = new Uint32Array(node_count);
237
+ node_cluster_assignments.fill(UNASSIGNED_CLUSTER);
238
+
239
+ /**
240
+ * build node index
241
+ * @type {Map<T, number>}
242
+ */
243
+ const node_index_map = new Map();
244
+
245
+ for (let i = 0; i < node_count; i++) {
246
+ node_index_map.set(node_array[i], i);
247
+ }
248
+
249
+ return graph_k_means_cluster_detailed(node_array, k, random_seed, node_cluster_assignments, node_index_map, graph);
250
+ }
@@ -626,9 +626,9 @@ function circleOverlapsConnection(circle, edge) {
626
626
  const dX = x1 - x0;
627
627
  const dY = y1 - y0;
628
628
 
629
- //Vector from center sphere to ray start
630
- const fX = x1 - cX;
631
- const fY = y1 - cY;
629
+ //Vector from ray start to circle center
630
+ const fX = x0 - cX;
631
+ const fY = y0 - cY;
632
632
 
633
633
  const a = dX * dX + dY * dY;
634
634
  const b = 2 * (fX * dX + fY * dY);
@@ -695,9 +695,9 @@ function resolveCircleLineOverlap(circle, c0, c1) {
695
695
  const dX = x1 - x0;
696
696
  const dY = y1 - y0;
697
697
 
698
- //Vector from center sphere to ray start
699
- const fX = x1 - cX;
700
- const fY = y1 - cY;
698
+ //Vector from ray start to circle center
699
+ const fX = x0 - cX;
700
+ const fY = y0 - cY;
701
701
 
702
702
  const a = dX * dX + dY * dY;
703
703
  const b = 2 * (fX * dX + fY * dY);
@@ -22,7 +22,7 @@ export class Connection {
22
22
  if (numPoints > 1) {
23
23
  let p0 = points[0];
24
24
  for (let i = 1; i < numPoints; i++) {
25
- let p1 = points[1];
25
+ let p1 = points[i];
26
26
 
27
27
  result += p0.distanceTo(p1);
28
28
 
@@ -1 +1 @@
1
- {"version":3,"file":"aabb2_force_into_container.d.ts","sourceRoot":"","sources":["../../../../../../src/core/graph/layout/box/aabb2_force_into_container.js"],"names":[],"mappings":"AA4CA;;;;GAIG;AACH,+EAMC"}
1
+ {"version":3,"file":"aabb2_force_into_container.d.ts","sourceRoot":"","sources":["../../../../../../src/core/graph/layout/box/aabb2_force_into_container.js"],"names":[],"mappings":"AA8CA;;;;GAIG;AACH,+EAMC"}
@@ -27,14 +27,16 @@ function interval_force_into_bounds(
27
27
  }
28
28
 
29
29
  }else{
30
- // too big to fit, pick the smallest displacement to occupy entire bounds
30
+ // too big to fit, shift so subject covers the entire bounds
31
31
 
32
32
  const a = subject_v0 - bounds_v0;
33
33
  const b = bounds_v1 - subject_v1;
34
34
 
35
- if(a > 0 && a < b){
35
+ if(a > 0){
36
+ // subject left edge is inside bounds, shift left so it covers
36
37
  displacement = -a;
37
38
  }else if(b > 0){
39
+ // subject right edge is inside bounds, shift right so it covers
38
40
  displacement = b;
39
41
  }
40
42
  }
@@ -6,5 +6,5 @@ import { computeStatisticalPartialMedian } from "./computeStatisticalPartialMedi
6
6
  * @returns {number}
7
7
  */
8
8
  export function computeStatisticalMedian(values) {
9
- return computeStatisticalPartialMedian(values, 0, values.length);
9
+ return computeStatisticalPartialMedian(values, 0, values.length - 1);
10
10
  }
@@ -10,20 +10,22 @@ export class NavigationMesh {
10
10
  * @param {BinaryTopology} source
11
11
  * @param {number} [agent_radius]
12
12
  * @param {number} [agent_height]
13
- * @param {number} [agent_max_climb] In radians, how steep of an angle can the agent go up by
13
+ * @param {number} [agent_max_climb_angle] In radians, how steep of an angle can the agent go up by
14
14
  * @param {Vector3} [up] Defines world's "UP" direction, this is what the agent will respect for climbing constraint
15
15
  */
16
- build({ source, agent_radius, agent_height, agent_max_climb, up, }: BinaryTopology): void;
16
+ build({ source, agent_radius, agent_height, agent_max_climb_angle, up, }: BinaryTopology): void;
17
17
  /**
18
+ * Compute a walkable path between the two points.
19
+ * The result is a sequence of 3d points written into `output`. First point matches start, last point matches goal (snapped onto the mesh).
18
20
  *
19
- * @param {Float32Array} output
21
+ * @param {Float32Array} output packed XYZ triples
20
22
  * @param {number} start_x
21
23
  * @param {number} start_y
22
24
  * @param {number} start_z
23
25
  * @param {number} goal_x
24
26
  * @param {number} goal_y
25
27
  * @param {number} goal_z
26
- * @returns {number}
28
+ * @returns {number} number of 3d points written to `output` (0 if no path was found)
27
29
  */
28
30
  find_path(output: Float32Array, start_x: number, start_y: number, start_z: number, goal_x: number, goal_y: number, goal_z: number): number;
29
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationMesh.d.ts","sourceRoot":"","sources":["../../../../../src/engine/navigation/mesh/NavigationMesh.js"],"names":[],"mappings":"AAmCA;IAEI,yBAAgC;IAEhC;;;OAGG;IACH,KAFU,GAAG,CAEG;IAEhB;;;;;;;OAOG;IACH,oEANW,cAAc,QAwBxB;IAGD;;;;;;;;;;OAUG;IACH,kBATW,YAAY,WACZ,MAAM,WACN,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,UACN,MAAM,GACJ,MAAM,CAwGlB;CAEJ;+BA3L4C,gEAAgE;oBAFzF,gCAAgC"}
1
+ {"version":3,"file":"NavigationMesh.d.ts","sourceRoot":"","sources":["../../../../../src/engine/navigation/mesh/NavigationMesh.js"],"names":[],"mappings":"AAgCA;IAEI,yBAAgC;IAEhC;;;OAGG;IACH,KAFU,GAAG,CAEG;IAEhB;;;;;;;OAOG;IACH,0EANW,cAAc,QAwBxB;IAGD;;;;;;;;;;;;OAYG;IACH,kBATW,YAAY,WACZ,MAAM,WACN,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,UACN,MAAM,GACJ,MAAM,CA+HlB;CAEJ;+BAlN4C,gEAAgE;oBADzF,gCAAgC"}