import {Node, Link, Layout} from './layout' import {GridRouter} from './gridrouter' import {Point} from './geom' /** * @property nudgeGap spacing between parallel edge segments * @property margin space around nodes * @property groupMargin space around groups */ export function gridify(pgLayout, nudgeGap: number, margin: number, groupMargin: number) { pgLayout.cola.start(0, 0, 0, 10, false); let gridrouter = route(pgLayout.cola.nodes(), pgLayout.cola.groups(), margin, groupMargin); return gridrouter.routeEdges(pgLayout.powerGraph.powerEdges, nudgeGap, e=> e.source.routerNode.id, e=> e.target.routerNode.id); } function route(nodes, groups, margin: number, groupMargin: number) { nodes.forEach(d => { d.routerNode = { name: d.name, bounds: d.bounds.inflate(-margin) }; }); groups.forEach(d => { d.routerNode = { bounds: d.bounds.inflate(-groupMargin), children: (typeof d.groups !== 'undefined' ? d.groups.map(c=> nodes.length + c.id) : []) .concat(typeof d.leaves !== 'undefined' ? d.leaves.map(c=> c.index) : []) }; }); let gridRouterNodes = nodes.concat(groups).map((d, i) => { d.routerNode.id = i; return d.routerNode; }); return new GridRouter(gridRouterNodes, { getChildren: (v: any) => v.children, getBounds: v => v.bounds }, margin - groupMargin); } export function powerGraphGridLayout( graph: { nodes: Node[], links: Link[] }, size: number[], grouppadding: number) { // compute power graph var powerGraph; graph.nodes.forEach((v,i) => (v).index = i); new Layout() .avoidOverlaps(false) .nodes(graph.nodes) .links(graph.links) .powerGraphGroups(function (d) { powerGraph = d; powerGraph.groups.forEach(v=> v.padding = grouppadding); }); // construct a flat graph with dummy nodes for the groups and edges connecting group dummy nodes to their children // power edges attached to groups are replaced with edges connected to the corresponding group dummy node var n = graph.nodes.length; var edges = []; var vs = graph.nodes.slice(0); vs.forEach((v, i) => (v).index = i); powerGraph.groups.forEach(g => { var sourceInd = g.index = g.id + n; vs.push(g); if (typeof g.leaves !== 'undefined') g.leaves.forEach(v => edges.push({ source: sourceInd, target: v.index })); if (typeof g.groups !== 'undefined') g.groups.forEach(gg => edges.push({ source: sourceInd, target: gg.id + n })); }); powerGraph.powerEdges.forEach(e=> { edges.push({ source: e.source.index, target: e.target.index }); }); // layout the flat graph with dummy nodes and edges new Layout() .size(size) .nodes(vs) .links(edges) .avoidOverlaps(false) .linkDistance(30) .symmetricDiffLinkLengths(5) .convergenceThreshold(1e-4) .start(100, 0, 0, 0, false); // final layout taking node positions from above as starting positions // subject to group containment constraints // and then gridifying the layout return { cola: new Layout() .convergenceThreshold(1e-3) .size(size) .avoidOverlaps(true) .nodes(graph.nodes) .links(graph.links) //.flowLayout('y', 30) .groupCompactness(1e-4) .linkDistance(30) .symmetricDiffLinkLengths(5) .powerGraphGroups(function (d) { powerGraph = d; powerGraph.groups.forEach(function (v) { v.padding = grouppadding }); }).start(50, 0, 100, 0, false), powerGraph: powerGraph }; }