Overview Wiki API Source

cola.js

Constraint-Based Layout in the Browser

cola.js (A.K.A. "WebCoLa") is an open-source JavaScript library for arranging your HTML5 documents and diagrams using constraint-based optimization techniques.

It works well with libraries like D3.js, svg.js, and Cytoscape.js. The core layout is based on a complete rewrite in Javascript of the C++ libcola library.

It also has an adaptor for d3.js that allows you to use cola as a drop-in replacement for the D3 force layout. The layout converges to a local optimum unlike the D3 force layout, which forces convergence through a simple annealing strategy. Thus, compared to D3 force layout:

However, it works well on an average machine on graphs with fewer than 100 nodes.

cola.js can now (optionally) route edges to avoid intersections with node boundaries.

Getting Started with D3

Replacing D3 force layout with cola.js is easy. Include the following in your html:

<script src="http://marvl.infotech.monash.edu/webcola/cola.v3.min.js"></script>

Or, if you don't like your javascript files being loaded from Australia, then host the cola.v3.min.js file locally.

Then replace D3 force with cola as follows:

D3 force setup: cola setup:
var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);
var d3cola = cola.d3adaptor()
    .linkDistance(30)
    .size([width, height]);

You then use cola as you would force (or just continue to call it force if you don't care what it's actually doing as much as me).

Note that while with D3 force layout you may have had to mess with parameters like "charge" to get reasonable node separation, cola should do a much better job of respecting the specified link distance in the final layout. This is because cola is directly trying to minimize the variance between ideal link distance and the actual distance in the drawing. In other words, just set linkDistance to something reasonable for the size of your nodes (in the same coordinate system).

There are some other optional parameters available. In the downward pointing edges example we kick off layout like so:

d3cola
    .nodes(graph.nodes)
    .links(graph.links)
    .constraints(graph.constraints)
    .symmetricDiffLinkLengths(5)
    .avoidOverlaps(true)
    .start(10,15,20);

We specify nodes and links exactly as in D3 force layout. However, constraints is a new parameter. It is an array containing constraints specified like so:

{"axis":"y", "left":0, "right":1, "gap":25}

This says that the center of graph.nodes[0] must be at least 25 pixels above the center of graph.nodes[1]. To be more precise, it is an inequality constraint that requires:

graph.nodes[0].y + gap <= graph.nodes[1].y

avoidOverlaps(true) dynamically generates constraints while layout progresses in order to prevent the bounding boxes of nodes from sliding over one another (see this example).

symmetricDiffLinkLengths(5) computes ideal lengths on each link to make extra space around high-degree nodes, using 5 as the basic length. Alternately, you can pass your own function f into linkDistance(f) that returns a specific length for each link (e.g. based on your data).

The start() method now includes up to three integer arguments. In the example above, start will initially apply 10 iterations of layout with no constraints, 15 iterations with only structural (user-specified) constraints and 20 iterations of layout with all constraints including anti-overlap constraints. Specifying such a schedule is useful to allow the graph to untangle before making it relatively "rigid" with constraints.

Click through the examples above to see other cola.js features in action.

I'm currently working hard to add more options and to support more classes of constraints, so please watch this space - and don't be shy about sending me feedback.

Using cola.js in cytoscape.js

Cytoscape.js has full support for Cola.js via an extension. Cytoscape.js has a full graph theory model, highly customisable styling, gesture support, and layouts — such that Cola.js can be run in Cytoscape.js in a single line of code:

cy.layout({ name: 'cola' /* and maybe some other options */ });