in tensorboard/plugins/graph/tf_graph_common/layout.ts [322:448]
function dagreLayout(
graph: graphlib.Graph,
params
): {height: number; width: number} {
_.extend(graph.graph(), {
nodesep: params.nodeSep,
ranksep: params.rankSep,
edgesep: params.edgeSep,
});
let bridgeNodeNames = [];
let nonBridgeNodeNames = [];
// Split out nodes into bridge and non-bridge nodes, and calculate the total
// width we should use for bridge nodes.
_.each(graph.nodes(), (nodeName) => {
let nodeInfo = graph.node(nodeName);
if (nodeInfo.node.type === NodeType.BRIDGE) {
bridgeNodeNames.push(nodeName);
} else {
nonBridgeNodeNames.push(nodeName);
}
});
// If there are no non-bridge nodes, then the graph has zero size.
if (!nonBridgeNodeNames.length) {
return {
width: 0,
height: 0,
};
}
dagre.layout(graph);
// Calculate the true bounding box of the graph by iterating over nodes and
// edges rather than accepting dagre's word for it. In particular, we should
// ignore the extra-wide bridge nodes and bridge edges, and allow for
// annotation boxes and labels.
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
_.each(nonBridgeNodeNames, (nodeName) => {
let nodeInfo = graph.node(nodeName);
let w = 0.5 * nodeInfo.width;
let x1 = nodeInfo.x - w;
let x2 = nodeInfo.x + w;
minX = x1 < minX ? x1 : minX;
maxX = x2 > maxX ? x2 : maxX;
// TODO: Account for the height of labels above op nodes here.
let h = 0.5 * nodeInfo.height;
let y1 = nodeInfo.y - h;
let y2 = nodeInfo.y + h;
minY = y1 < minY ? y1 : minY;
maxY = y2 > maxY ? y2 : maxY;
});
_.each(graph.edges(), (edgeObj) => {
let edgeInfo = graph.edge(edgeObj);
if (edgeInfo.structural) {
return; // Skip structural edges from min/max calculations.
}
// Since the node size passed to dagre includes the in and out
// annotations, the endpoints of the edge produced by dagre may not
// point to the actual node shape (rectangle, ellipse). We correct the
// end-points by finding the intersection of a line between the
// next-to-last (next-to-first) point and the destination (source)
// rectangle.
let sourceNode = graph.node(edgeInfo.metaedge.v);
let destNode = graph.node(edgeInfo.metaedge.w);
// Straight 3-points edges are special case, since they are curved after
// our default correction. To keep them straight, we remove the mid point
// and correct the first and the last point to be the center of the
// source and destination node respectively.
if (edgeInfo.points.length === 3 && isStraightLine(edgeInfo.points)) {
if (sourceNode != null) {
let cxSource = sourceNode.expanded
? sourceNode.x
: computeCXPositionOfNodeShape(sourceNode as any);
edgeInfo.points[0].x = cxSource;
}
if (destNode != null) {
let cxDest = destNode.expanded
? destNode.x
: computeCXPositionOfNodeShape(destNode as any);
edgeInfo.points[2].x = cxDest;
}
// Remove the middle point so the edge doesn't curve.
edgeInfo.points = [edgeInfo.points[0], edgeInfo.points[1]];
}
// Correct the destination endpoint of the edge.
let nextToLastPoint = edgeInfo.points[edgeInfo.points.length - 2];
// The destination node might be null if this is a bridge edge.
if (destNode != null) {
edgeInfo.points[edgeInfo.points.length - 1] = intersectPointAndNode(
nextToLastPoint,
destNode as any
);
}
// Correct the source endpoint of the edge.
let secondPoint = edgeInfo.points[1];
// The source might be null if this is a bridge edge.
if (sourceNode != null) {
edgeInfo.points[0] = intersectPointAndNode(
secondPoint,
sourceNode as any
);
}
_.each(edgeInfo.points, (point: render.Point) => {
minX = point.x < minX ? point.x : minX;
maxX = point.x > maxX ? point.x : maxX;
minY = point.y < minY ? point.y : minY;
maxY = point.y > maxY ? point.y : maxY;
});
});
// Shift all nodes and edge points to account for the left-padding amount,
// and the invisible bridge nodes.
_.each(graph.nodes(), (nodeName) => {
let nodeInfo = graph.node(nodeName);
nodeInfo.x -= minX;
nodeInfo.y -= minY;
});
_.each(graph.edges(), (edgeObj) => {
_.each(graph.edge(edgeObj).points, (point: render.Point) => {
point.x -= minX;
point.y -= minY;
});
});
return {
width: maxX - minX,
height: maxY - minY,
};
}