Cluster gravity: per-cluster centroid pull force in the engine

The 'virtual node' realized as a centroid force: two allocation-free
O(n) passes per tick (membership resolved once at sim creation;
Float64Array sums). Measured indistinguishable from force-off at 338
nodes (271ms vs 272ms per 400-tick settle).

- createGraphSimulation: SimOptions.clusterPull + linkStrength +
  velocityDecay (physics controls plumbing); degree-scaled link
  stiffness so linkStrength=1 is exactly d3's default
- settleGraphLayout split into layout-settle.ts (it computes the
  Louvain partition for the pull); layout.ts stays browser-barrel-pure
- barrel no longer re-exports clusters.ts: graphology + louvain are
  fully out of the client bundle (verified against built chunks)
- replay passes the per-batch partition; CLUSTER_PULL_STRENGTH=0.15
  tuned by sweep: cluster spread 63->36 world units, centroid
  separation and total span unchanged (no blob merge, no collapse)
This commit is contained in:
amosblomqvist
2026-09-13 09:52:12 +03:00
parent 60686d7831
commit 975a78a304
7 changed files with 183 additions and 41 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ import type {
SessionFindings,
} from "../../core/contracts.ts";
import { getGraph, mergeFindings, type EmissionStats, type GraphDeps } from "../../core/graph.ts";
import { settleGraphLayout } from "../../core/layout.ts";
import { settleGraphLayout } from "../../core/layout-settle.ts";
import { refreshClusterLabels } from "./cluster-namer.ts";
/**