JointJS+ TreeLayout

You may see the name Rappid in our documentation, code and other materials. Rappid has been replaced by a new brand, JointJS+. This change has no effect on functionality. To avoid confusion, please consider Rappid and JointJS+ as synonyms. Read more here.

layout.TreeLayout

layout.TreeLayout is a layout algorithm for tree-like graphs. It's great for displaying org charts, mind maps, class hierarchies and other tree structures. The layout.TreeLayout is strong in combination with the ui.TreeLayoutView which implements drag&drop functionality useful when editing a tree structure.

Installation

Include joint.layout.treeLayout.js file to your HTML:

<script src="joint.layout.treeLayout.js"></script>

Usage

Create an object of layout.TreeLayout type passing your graph (object of type joint.dia.Graph) as a parameter. Then you can just call the layout() method and the graph will be automatically laid out. It's important to note that the position of the root element of the tree will stay the same as it was before the layout. This allows you to move the tree to a position you want or have your tree be laid out around the root.

var graphLayout = new joint.layout.TreeLayout({
    graph: graph,
    parentGap: 20,
    siblingGap: 20
});

// Root element position stays the same after the layout.
// In this demo, we assume the root is the first element.
var root = graph.getElements()[0].position(200, 200);

graphLayout.layout();

Configuration

The following table lists options that you can pass to the layout.TreeLayout constructor function:

graph The JointJS graph object on which you want to perform the layout.
parentGap The distance between a parent element and its children. Defaults to 20.
siblingGap The distance between siblings. Defaults to 20.
firstChildGap The distance between the first sibling and its parent. It's applicable only for diagonal layouts ('TL', 'TR','BL', 'BR'). It defaults to 20.
direction The default direction of the layout. It can be set to one of the following values: 'L', 'R', 'T', 'B' (for left-to-right, right-to-left, top-to-bottom and bottom-to-top layouts) or diagonal variants 'TL', 'TR','BL', 'BR' (for top-left, top-right, bottom-left and bottom-right). It defaults to 'R'.

The direction defines how an element connects its parent. For instance element.set('direction', 'R') will appear on the right side to the parent element. Note that, this option is only used for nodes that do not have a direction property explicitly set.

filter(children, parent, opt)

An arbitrary element or sub-tree can be skipped by the layout. By providing a filter function, one can control, which elements will be laid out and which not.

The filter function returns an array of elements to be laid out and runs for every single parent element in the graph.

Parameters:
  • children is an array of siblings
  • parent is the parent of the children or null, when children are roots
  • opt is the option object passed to layout() method when calling the layout.

// e.g. a collapse branch is not visible in the paper but resides in the graph.
function(children) {
    return children.filter(function(child) {
        return child.get('visible');
    });
}

No element is filtered out by default.

updatePosition(element, position, opt) A function responsible for setting the resulting position on the elements. It calls element.set('position', position, opt); by default. It can be used for positioning the elements in an animated manner. For instance:
element.transition('position', position, {
    delay: 300,
    valueFunction: joint.util.interpolate.object,
});
updateVertices(link, vertices, opt) A function responsible for setting the resulting vertices on the links. It calls link.set('vertices', vertices, opt); by default. If the option is defined as null no vertices will be set by the layout.
attributeNames An object that maps element attributes accepted by the layout to user defined attribute names. Useful for resolving conflicts when an attribute is already in use by the application logic. e.g Setting { siblingRank: 'index' } will make the layout look for the index attribute instead of siblingRank when trying to figure out the order of siblings.

The tree layout also reads some element properties allowing for a fine control of the layout engine. These are:

direction

The direction of the layout for this specific node. Note the tree layout can handle even mixed layout directions on a per-node basis.

See the globally defined direction in the TreeLayout constructor function configuration.

siblingRank The index of the element among its siblings.
offset The additional distance from the parent (the real distance is a sum of parentGap - defined globally in the TreeLayout constructor function configuration. - and the offset).
margin Extends the area occupied by the element on both sibling sides.
element.set({
    direction: 'R',
    size: { width: 100, height: 100 },
    margin: 20
});
// as the `element` siblings are located above and under the element,
// it will in fact occupied an area of { width: 100, height: 140 }
prevSiblingGap The additional gap next to the element sub-tree on the side of the previous sibling. The previous sibling is defined as the sibling with the closest smaller siblingRank.
nextSiblingGap The additional gap next to the element sub-tree on the side of the next sibling. The next sibling is defined as the sibling with the closest larger siblingRank.
firstChildGap The distance between the element and the first child. It's applicable only for diagonal layouts ('TL', 'TR','BL', 'BR'). It overides the global firstChildGap option.

API

layout([opt])

Layout the graph in a tree. It iterates over all graph sources (elements without a parent) and layout them as separate trees.

The option object opt, which will be passed down to all operations on the graph made by the layout call. e.g. treeLayout.layout({ myFlag: true }) will internally set an element position as element.position(x, y, { myFlag: true });.

getLayoutBBox()

Returns the bounding box of all affected elements from the last layout run (i.e. elements, which have been filtered are not taken into account when calculating the bounding box).