Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active October 6, 2020 07:33
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/7733be2b2e595cd17189 to your computer and use it in GitHub Desktop.
Save nitaku/7733be2b2e595cd17189 to your computer and use it in GitHub Desktop.
Cola.js layout

A simple exercise with cola.js. A node-link diagram is displayed, based on positions computed by cola's fast-converging constraints-based layout algorithm. Handling of more than one connected component, node dragging and non-overlapping constraints are also shown.

Compare with a similar example using native d3.js force-directed layout.

graph = {
nodes: [
{id: 'A'},
{id: 'B'},
{id: 'C'},
{id: 'D'},
{id: 'E'},
{id: 'F'},
{id: 'G'},
{id: 'H'},
{id: 'I'},
{id: 'J'},
{id: 'K'},
{id: 'L'},
{id: 'M'},
{id: 'N'},
{id: 'O'}
],
links: [
{id: 1, source: 'A', target: 'B'},
{id: 2, source: 'B', target: 'C'},
{id: 3, source: 'C', target: 'A'},
{id: 4, source: 'B', target: 'D'},
{id: 5, source: 'D', target: 'C'},
{id: 6, source: 'D', target: 'E'},
{id: 7, source: 'E', target: 'F'},
{id: 8, source: 'F', target: 'G'},
{id: 9, source: 'F', target: 'H'},
{id: 10, source: 'G', target: 'H'},
{id: 11, source: 'G', target: 'I'},
{id: 12, source: 'H', target: 'I'},
{id: 13, source: 'J', target: 'E'},
{id: 14, source: 'J', target: 'L'},
{id: 15, source: 'J', target: 'K'},
{id: 16, source: 'K', target: 'L'},
{id: 17, source: 'L', target: 'M'},
{id: 18, source: 'M', target: 'K'},
{id: 19, source: 'N', target: 'O'}
]}
### objectify the graph ###
### resolve node IDs (not optimized at all!) ###
for l in graph.links
for n in graph.nodes
if l.source is n.id
l.source = n
if l.target is n.id
l.target = n
R = 18
svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
### create nodes and links ###
links = svg.selectAll('.link')
.data(graph.links, (d) -> d.id)
links
.enter().append('line')
.attr('class', 'link')
nodes = svg.selectAll('.node')
.data(graph.nodes, (d) -> d.id)
enter_nodes = nodes.enter().append('g')
.attr('class', 'node')
enter_nodes.append('circle')
.attr('r', R)
### draw the label ###
enter_nodes.append('text')
.text((d) -> d.id)
.attr('dy', '0.35em')
### cola layout ###
graph.nodes.forEach (v) ->
v.width = 2.5*R
v.height = 2.5*R
d3cola = cola.d3adaptor()
.size([width, height])
.linkDistance(50)
.avoidOverlaps(true)
.nodes(graph.nodes)
.links(graph.links)
.on 'tick', () ->
### update nodes and links ###
nodes
.attr('transform', (d) -> "translate(#{d.x},#{d.y})")
links
.attr('x1', (d) -> d.source.x)
.attr('y1', (d) -> d.source.y)
.attr('x2', (d) -> d.target.x)
.attr('y2', (d) -> d.target.y)
enter_nodes
.call(d3cola.drag)
d3cola.start(30,30,30)
.node > circle {
fill: #dddddd;
stroke: #777777;
stroke-width: 2px;
}
.node > text {
font-family: sans-serif;
text-anchor: middle;
pointer-events: none;
}
.link {
stroke: #dddddd;
stroke-width: 4px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cola.js layout</title>
<link rel="stylesheet" href="index.css">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://marvl.infotech.monash.edu/webcola/cola.v3.min.js"></script>
</head>
<body>
<svg width="960px" height="500px"></svg>
<script src="index.js"></script>
</body>
</html>
// Generated by CoffeeScript 1.4.0
(function() {
var R, d3cola, enter_nodes, graph, height, l, links, n, nodes, svg, width, _i, _j, _len, _len1, _ref, _ref1;
graph = {
nodes: [
{
id: 'A'
}, {
id: 'B'
}, {
id: 'C'
}, {
id: 'D'
}, {
id: 'E'
}, {
id: 'F'
}, {
id: 'G'
}, {
id: 'H'
}, {
id: 'I'
}, {
id: 'J'
}, {
id: 'K'
}, {
id: 'L'
}, {
id: 'M'
}, {
id: 'N'
}, {
id: 'O'
}
],
links: [
{
id: 1,
source: 'A',
target: 'B'
}, {
id: 2,
source: 'B',
target: 'C'
}, {
id: 3,
source: 'C',
target: 'A'
}, {
id: 4,
source: 'B',
target: 'D'
}, {
id: 5,
source: 'D',
target: 'C'
}, {
id: 6,
source: 'D',
target: 'E'
}, {
id: 7,
source: 'E',
target: 'F'
}, {
id: 8,
source: 'F',
target: 'G'
}, {
id: 9,
source: 'F',
target: 'H'
}, {
id: 10,
source: 'G',
target: 'H'
}, {
id: 11,
source: 'G',
target: 'I'
}, {
id: 12,
source: 'H',
target: 'I'
}, {
id: 13,
source: 'J',
target: 'E'
}, {
id: 14,
source: 'J',
target: 'L'
}, {
id: 15,
source: 'J',
target: 'K'
}, {
id: 16,
source: 'K',
target: 'L'
}, {
id: 17,
source: 'L',
target: 'M'
}, {
id: 18,
source: 'M',
target: 'K'
}, {
id: 19,
source: 'N',
target: 'O'
}
]
};
/* objectify the graph
*/
/* resolve node IDs (not optimized at all!)
*/
_ref = graph.links;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
l = _ref[_i];
_ref1 = graph.nodes;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
n = _ref1[_j];
if (l.source === n.id) {
l.source = n;
}
if (l.target === n.id) {
l.target = n;
}
}
}
R = 18;
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
/* create nodes and links
*/
links = svg.selectAll('.link').data(graph.links, function(d) {
return d.id;
});
links.enter().append('line').attr('class', 'link');
nodes = svg.selectAll('.node').data(graph.nodes, function(d) {
return d.id;
});
enter_nodes = nodes.enter().append('g').attr('class', 'node');
enter_nodes.append('circle').attr('r', R);
/* draw the label
*/
enter_nodes.append('text').text(function(d) {
return d.id;
}).attr('dy', '0.35em');
/* cola layout
*/
graph.nodes.forEach(function(v) {
v.width = 2.5 * R;
return v.height = 2.5 * R;
});
d3cola = cola.d3adaptor().size([width, height]).linkDistance(50).avoidOverlaps(true).nodes(graph.nodes).links(graph.links).on('tick', function() {
/* update nodes and links
*/
nodes.attr('transform', function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
return links.attr('x1', function(d) {
return d.source.x;
}).attr('y1', function(d) {
return d.source.y;
}).attr('x2', function(d) {
return d.target.x;
}).attr('y2', function(d) {
return d.target.y;
});
});
enter_nodes.call(d3cola.drag);
d3cola.start(30, 30, 30);
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment