|
// Generated by CoffeeScript 1.4.0 |
|
(function() { |
|
var PAD_MULTIPLIER, R, d3cola, defs, enter_nodes, graph, height, i, l, levels, links, n, nn, nodes, svg, topological_order, width, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _len7, _m, _n, _o, _p, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; |
|
|
|
graph = { |
|
nodes: [ |
|
{ |
|
id: 'A' |
|
}, { |
|
id: 'B' |
|
}, { |
|
id: 'C' |
|
}, { |
|
id: 'D' |
|
}, { |
|
id: 'E' |
|
}, { |
|
id: 'F' |
|
}, { |
|
id: 'G' |
|
}, { |
|
id: 'H' |
|
}, { |
|
id: 'I' |
|
}, { |
|
id: 'J' |
|
}, { |
|
id: 'Z' |
|
} |
|
], |
|
links: [ |
|
{ |
|
id: 1, |
|
source: 'A', |
|
target: 'B' |
|
}, { |
|
id: 2, |
|
source: 'A', |
|
target: 'C' |
|
}, { |
|
id: 3, |
|
source: 'A', |
|
target: 'D' |
|
}, { |
|
id: 4, |
|
source: 'B', |
|
target: 'E' |
|
}, { |
|
id: 5, |
|
source: 'B', |
|
target: 'F' |
|
}, { |
|
id: 6, |
|
source: 'C', |
|
target: 'G' |
|
}, { |
|
id: 7, |
|
source: 'C', |
|
target: 'F' |
|
}, { |
|
id: 8, |
|
source: 'F', |
|
target: 'G' |
|
}, { |
|
id: 9, |
|
source: 'G', |
|
target: 'H' |
|
}, { |
|
id: 10, |
|
source: 'G', |
|
target: 'I' |
|
}, { |
|
id: 11, |
|
source: 'H', |
|
target: 'I' |
|
}, { |
|
id: 12, |
|
source: 'I', |
|
target: 'J' |
|
}, { |
|
id: 13, |
|
source: 'Z', |
|
target: 'B' |
|
} |
|
] |
|
}; |
|
|
|
/* 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; |
|
} |
|
} |
|
} |
|
|
|
/* store the node index into the node itself |
|
*/ |
|
|
|
|
|
_ref2 = graph.nodes; |
|
for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) { |
|
n = _ref2[i]; |
|
n.i = i; |
|
} |
|
|
|
/* store neighbor nodes into each node |
|
*/ |
|
|
|
|
|
_ref3 = graph.nodes; |
|
for (i = _l = 0, _len3 = _ref3.length; _l < _len3; i = ++_l) { |
|
n = _ref3[i]; |
|
n.in_neighbors = []; |
|
n.out_neighbors = []; |
|
} |
|
|
|
_ref4 = graph.links; |
|
for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { |
|
l = _ref4[_m]; |
|
l.source.out_neighbors.push(l.target); |
|
l.target.in_neighbors.push(l.source); |
|
} |
|
|
|
/* compute longest distances |
|
*/ |
|
|
|
|
|
topological_order = tsort(graph.links.map(function(l) { |
|
return [l.source.i, l.target.i]; |
|
})); |
|
|
|
_ref5 = graph.nodes; |
|
for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) { |
|
n = _ref5[_n]; |
|
n.longest_dist = -Infinity; |
|
} |
|
|
|
graph.nodes[0].longest_dist = 0; |
|
|
|
graph.nodes[10].longest_dist = 0; |
|
|
|
for (_o = 0, _len6 = topological_order.length; _o < _len6; _o++) { |
|
i = topological_order[_o]; |
|
n = graph.nodes[i]; |
|
_ref6 = n.out_neighbors; |
|
for (_p = 0, _len7 = _ref6.length; _p < _len7; _p++) { |
|
nn = _ref6[_p]; |
|
if (nn.longest_dist < n.longest_dist + 1) { |
|
nn.longest_dist = n.longest_dist + 1; |
|
} |
|
} |
|
} |
|
|
|
graph.constraints = []; |
|
|
|
/* create the alignment contraints |
|
*/ |
|
|
|
|
|
levels = _.uniq(graph.nodes.map(function(n) { |
|
return n.longest_dist; |
|
})); |
|
|
|
levels.forEach(function(depth) { |
|
return graph.constraints.push({ |
|
type: 'alignment', |
|
axis: 'y', |
|
offsets: graph.nodes.filter(function(n) { |
|
return n.longest_dist === depth; |
|
}).map(function(n) { |
|
return { |
|
node: n.i, |
|
offset: 0 |
|
}; |
|
}) |
|
}); |
|
}); |
|
|
|
R = 18; |
|
|
|
PAD_MULTIPLIER = 3.5; |
|
|
|
/* create the position contraints |
|
*/ |
|
|
|
|
|
levels.forEach(function(depth, i) { |
|
var n1, n2; |
|
if (i < levels.length - 1) { |
|
n1 = _.find(graph.nodes, function(n) { |
|
return n.longest_dist === depth; |
|
}); |
|
n2 = _.find(graph.nodes, function(n) { |
|
return n.longest_dist === depth + 1; |
|
}); |
|
return graph.constraints.push({ |
|
axis: 'y', |
|
left: n1.i, |
|
right: n2.i, |
|
gap: 2 * R |
|
}); |
|
} |
|
}); |
|
|
|
svg = d3.select('svg'); |
|
|
|
width = svg.node().getBoundingClientRect().width; |
|
|
|
height = svg.node().getBoundingClientRect().height; |
|
|
|
defs = svg.append('defs'); |
|
|
|
/* define arrow markers for graph links |
|
*/ |
|
|
|
|
|
defs.append('marker').attr({ |
|
id: 'end-arrow', |
|
viewBox: '0 0 10 10', |
|
refX: 4 + R, |
|
refY: 5, |
|
orient: 'auto' |
|
}).append('path').attr({ |
|
d: 'M0,0 L0,10 L10,5 z' |
|
}); |
|
|
|
/* 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 = PAD_MULTIPLIER * R; |
|
return v.height = PAD_MULTIPLIER * R; |
|
}); |
|
|
|
d3cola = cola.d3adaptor().size([width, height]).linkDistance(60).constraints(graph.constraints).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(100, 30, 30); |
|
|
|
}).call(this); |