Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion graph-ui/src/hooks/useGraphData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("fetchLayout", () => {

await fetchLayout("large-project");

expect(GRAPH_RENDER_NODE_LIMIT).toBe(2000);
expect(GRAPH_RENDER_NODE_LIMIT).toBe(60000);
expect(fetchMock).toHaveBeenCalledTimes(1);
const calls = fetchMock.mock.calls as unknown as Array<[string]>;
const [url] = calls[0];
Expand Down
2 changes: 1 addition & 1 deletion graph-ui/src/hooks/useGraphData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface UseGraphDataResult {
fetchDetail: (project: string, centerNode: string) => void;
}

export const GRAPH_RENDER_NODE_LIMIT = 2000;
export const GRAPH_RENDER_NODE_LIMIT = 60000;

export async function fetchLayout(
project: string,
Expand Down
29 changes: 23 additions & 6 deletions src/ui/layout3d.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@

/* ── Constants ────────────────────────────────────────────────── */

#define DEFAULT_MAX_NODES 2000
#define HARD_MAX_NODES 10000
#define DEFAULT_MAX_NODES 60000
#define HARD_MAX_NODES 200000
#define BH_THETA 1.2f
#define OCTREE_MAX_DEPTH 26 /* stop subdividing coincident points (OOM guard) */
#define OCTREE_MIN_HALF 1e-4f /* minimum octree cell half-size */

/* Local optimization: gentle, preserves structure */
#define LOCAL_REPULSION 8.0f
Expand Down Expand Up @@ -175,7 +177,8 @@ static void child_center(octree_node_t *n, int o, float *cx, float *cy, float *c
*cy = n->oy + ((o & 2) ? q : -q);
*cz = n->oz + ((o & 4) ? q : -q);
}
static void octree_insert(octree_node_t *n, int idx, float x, float y, float z, float mass) {
static void octree_insert(octree_node_t *n, int idx, float x, float y, float z, float mass,
int depth) {
if (n->total_mass == 0.0f && n->body_index == -1) {
n->body_index = idx;
n->body_mass = mass;
Expand All @@ -185,6 +188,20 @@ static void octree_insert(octree_node_t *n, int idx, float x, float y, float z,
n->total_mass = mass;
return;
}
/* OOM guard: when bodies share (or nearly share) a position, subdivision
* never separates them, so half_size shrinks toward zero and we allocate
* octree cells without bound — the runaway that exhausted memory on large
* graphs. Once we hit the depth/size floor, stop splitting and fold the body
* into this cell as an aggregate (mass-weighted centroid). */
if (depth >= OCTREE_MAX_DEPTH || n->half_size < OCTREE_MIN_HALF) {
float nm = n->total_mass + mass;
n->cx = (n->cx * n->total_mass + x * mass) / nm;
n->cy = (n->cy * n->total_mass + y * mass) / nm;
n->cz = (n->cz * n->total_mass + z * mass) / nm;
n->total_mass = nm;
n->body_index = -1;
return;
}
if (n->body_index >= 0) {
int oi = n->body_index;
float ox = n->cx, oy = n->cy, oz = n->cz, om = n->body_mass;
Expand All @@ -196,7 +213,7 @@ static void octree_insert(octree_node_t *n, int idx, float x, float y, float z,
n->children[o] = octree_new(a, b, c, n->half_size * 0.5f);
}
if (n->children[o])
octree_insert(n->children[o], oi, ox, oy, oz, om);
octree_insert(n->children[o], oi, ox, oy, oz, om, depth + 1);
}
float nm = n->total_mass + mass;
n->cx = (n->cx * n->total_mass + x * mass) / nm;
Expand All @@ -210,7 +227,7 @@ static void octree_insert(octree_node_t *n, int idx, float x, float y, float z,
n->children[o] = octree_new(a, b, c, n->half_size * 0.5f);
}
if (n->children[o])
octree_insert(n->children[o], idx, x, y, z, mass);
octree_insert(n->children[o], idx, x, y, z, mass, depth + 1);
}
static void octree_repulse(octree_node_t *n, float px, float py, float pz, float mm, int si,
float kr, float *fx, float *fy, float *fz) {
Expand Down Expand Up @@ -274,7 +291,7 @@ static void local_optimize(body_t *b, int n, const int *es, const int *ed, int n
if (!root)
break;
for (int i = 0; i < n; i++)
octree_insert(root, i, b[i].x, b[i].y, b[i].z, b[i].mass);
octree_insert(root, i, b[i].x, b[i].y, b[i].z, b[i].mass, 0);
for (int i = 0; i < n; i++)
octree_repulse(root, b[i].x, b[i].y, b[i].z, b[i].mass, i, LOCAL_REPULSION, &b[i].fx,
&b[i].fy, &b[i].fz);
Expand Down
Loading