Automatic layout of directed graphs. This plugin uses the open-source (MIT license) Dagre library internally. It provides a wrapper so that you can call the layout directly on JointJS graphs.

Note that you must include both the Dagre and Graphlib libraries as dependencies if you wish to use the layout.DirectedGraph plugin.

Usage

joint.layout.DirectedGraph plugin exposes the joint.layout.DirectedGraph.layout(graphOrElements, opt) function. The first parameter graphOrElements is the joint.dia.Graph or an array of joint.dia.Elements we want to layout. The second parameter options is an object that contains various options for configuring the layout.

var graphBBox = joint.layout.DirectedGraph.layout(graph, {
    nodeSep: 50,
    edgeSep: 80,
    rankDir: "TB"
});
console.log('x:', graphBBox.x, 'y:', graphBBox.y)
console.log('width:', graphBBox.width, 'height:', graphBBox.height);

A full blog post explaining this example in more detail can be found here.

Example with clusters

Configuration

The following table lists options that you can pass to the joint.layout.DirectedGraph.layout(graph, opt) function:

nodeSepa number of pixels representing the separation between adjacent nodes in the same rank
edgeSepa number of pixels representing the separation between adjacent edges in the same rank
rankSepa number of pixels representing the separation between ranks
rankDirdirection of the layout (one of "TB" (top-to-bottom) / "BT" (bottom-to-top) / "LR" (left-to-right) / "RL" (right-to-left))
marginXnumber of pixels to use as a margin around the left and right of the graph.
marginYnumber of pixels to use as a margin around the top and bottom of the graph.
rankerType of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex' (default), 'tight-tree' or 'longest-path'. number of pixels to use as a margin around the top and bottom of the graph.
resizeClustersset to false if you don't want parent elements to stretch in order to fit all their embedded children. Default is true.
clusterPaddingA gap between the parent element and the boundary of its embedded children. It could be a number or an object e.g. { left: 10, right: 10, top: 30, bottom: 10 }. It defaults to 10.
setPosition(element, position)a function that will be used to set the position of elements at the end of the layout. This is useful if you don't want to use the default element.set('position', position) but want to set the position in an animated fashion via transitions.
setVertices(link, vertices) If set to true the layout will adjust the links by setting their vertices. It defaults to false. If the option is defined as a function it will be used to set the vertices of links at the end of the layout. This is useful if you don't want to use the default link.set('vertices', vertices) but want to set the vertices in an animated fashion via transitions.
setLabels(link, labelPosition, points) If set to true the layout will adjust the labels by setting their position. It defaults to false. If the option is defined as a function it will be used to set the labels of links at the end of the layout. Note: Only the first label (link.label(0);) is positioned by the layout.
dagre Optional. The DirectedGraph plugin has the dependency to the dagre lay-outing tool. The DirectedGraph is expecting to have the dagre variable in the global namespace, you can, however, pass your own instance of the dagre using this property.
graphlib Optional. The DirectedGraph plugin has the dependency to the graphlib - a directed multi-graph library. The DirectedGraph is expecting to have the grpahlib variable in the global namespace, you can, however, pass your own instance of the graphlib using this property.
exportElement(element) Convert element attributes into dagre node attributes. By default, it returns the element attributes below.
exportLink(link) Convert link attributes into dagre edge attributes. By default, it returns the link attributes below.

Additionally, the layout engine takes into account some properties on elements/links to fine tune the layout further. These are:

size element An object with `width` and `height` properties representing the size of the element.
minLen link The number of ranks to keep between the source and target of the link.
weight link The weight to assign edges. Higher weight edges are generally made shorter and straighter than lower weight edges.
labelPosition link Where to place the label relative to the edge. 'l' = left, 'c' = center (default), 'r' = right.
labelOffset link How many pixels to move the label away from the edge. Applies only when labelPosition is left or right.
labelSize link The width and height of the edge label in pixels. e.g. { width: 100, height: 50 }

The layout() function returns a bounding box (g.Rect) of the resulting graph.

API

The layout.DirectedGraph plugins extends the joint.dia.Graph type with functions that make it easy to convert graphs to and from the Graphlib graph format. This allows you to easily use many of the graph algorithms provided by this library.

toGraphLib()

Convert the JointJS joint.dia.Graph object to the Graphlib graph object.

var graph = new joint.dia.Graph;
// ... populated the graph with elements connected with links
graphlib.alg.isAcyclic(graph.toGraphLib())      // true if the graph is acyclic
Please note, the toGraphLib has the dependency to the graphlib - a directed multi-graph library. You can either have the graphlib in the global namespace or you can pass the graphlib instance as an option property.

fromGraphLib(glGraph, opt)

Convert the Graphlib graph representation to JointJS joint.dia.Graph object. opt.importNode and opt.importEdge are functions that accept a Graphlib node and edge objects and should return JointJS element/link.

var g = new graphlib.Graph();
g.setNode(1);
g.setNode(2);
g.setNode(3);
g.setEdge(1, 2);
g.setEdge(2, 3);

var graph = new joint.dia.Graph;
graph.fromGraphLib(g, {
    importNode: function(node) {
        return new joint.shapes.basic.Rect({
            position: { x: node.x, y: node.y },
            size: { width: node.width, height: node.height }
        });
    },
    importEdge: function(edge) {
        return new joint.dia.Link({
            source: { id: edge.v },
            target: { id: edge.w }
        });
    }
});