diff --git a/graph-ui/src/galaxy/graph-scope.test.ts b/graph-ui/src/galaxy/graph-scope.test.ts index 9780e772c..6fe3448a0 100644 --- a/graph-ui/src/galaxy/graph-scope.test.ts +++ b/graph-ui/src/galaxy/graph-scope.test.ts @@ -215,6 +215,14 @@ it('wraps large hierarchy levels into readable blocks without losing or overlapp } expect(result.data.nodes.every(node => node.color === '#abcabc')).toBe(true); }); +it('takes hop columns from discovered levels, not from the global layout depth of a preview', () => { + // A preview from the overall layout keeps server coordinates; z says nothing about hops. + const nodes = [{ ...node(1), z: -250 }, { ...node(2), z: 40 }, { ...node(3), z: -90 }]; + const levels = new Map([[1, 0], [2, 1], [3, 2]]); + const result = scopedHierarchy({ data: { nodes, edges: [], total_nodes: 3 }, roots: new Set([1]), depth: 2, exhausted: false, levels }, 'preview'); + expect(Object.fromEntries(result.placements.map(placement => [placement.name, placement.hop]))).toEqual({ n1: 0, n2: 1, n3: 2 }); + expect(result.depth).toBe(3); +}); it('retains simple columns for small hierarchy levels', () => { const nodes = [{ ...node(1), z: 0 }, { ...node(2), z: -18 }, { ...node(3), z: -18 }]; const result = scopedHierarchy({ data: { nodes, edges: [], total_nodes: 3 }, roots: new Set([1]), depth: 1, exhausted: false }, 'small'); diff --git a/graph-ui/src/galaxy/graph-scope.ts b/graph-ui/src/galaxy/graph-scope.ts index b4f823eaf..902c3c648 100644 --- a/graph-ui/src/galaxy/graph-scope.ts +++ b/graph-ui/src/galaxy/graph-scope.ts @@ -208,11 +208,14 @@ export function limitGraphRender(data: GraphData, nodeLimit: number, edgeLimit: /** The same evidence in successive hop blocks. Small hops keep one column; * large hops wrap into compact grids so fitting thousands of neighbors does - * not turn the entire scene into a single vertical line. */ + * not turn the entire scene into a single vertical line. + * + * Hops come from the discovered levels. Only arranged rings encode them in z; + * a preview built from the overall layout keeps its global coordinates. */ export function scopedHierarchy(scope: ScopedGraph, name: string): import('./hierarchy-layout').HierarchyProjection { const columns = new Map(); for (const node of scope.data.nodes) { - const hop = Math.max(0, Math.round(-node.z / 18)); + const hop = scope.roots.has(node.id) ? 0 : scope.levels?.get(node.id) ?? Math.max(0, Math.round(-node.z / 18)); const entries = columns.get(hop) ?? []; entries.push(node); columns.set(hop, entries); } const nodes: GraphNode[] = [], remap = new Map();