/** * `dc_graph.dagre_layout` is an adaptor for dagre.js layouts in dc.graph.js * * In addition to the below layout attributes, `dagre_layout` also implements the attributes from * {@link dc_graph.graphviz_attrs graphviz_attrs} * @class dagre_layout * @memberof dc_graph * @param {String} [id=uuid()] - Unique identifier * @return {dc_graph.dagre_layout} **/ dc_graph.dagre_layout = function(id) { var _layoutId = id || uuid(); var _dagreGraph = null, _tick, _done; var _dispatch = d3.dispatch('tick', 'start', 'end'); // node and edge objects preserved from one iteration // to the next (as long as the object is still in the layout) var _nodes = {}, _edges = {}; function init(options) { // Create a new directed graph _dagreGraph = new dagre.graphlib.Graph({multigraph: true, compound: true}); // Set an object for the graph label _dagreGraph.setGraph({rankdir: options.rankdir, nodesep: options.nodesep, ranksep: options.ranksep}); // Default to assigning a new object as a label for each new edge. _dagreGraph.setDefaultEdgeLabel(function() { return {}; }); } function data(nodes, edges, clusters) { var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { return v.dcg_nodeKey; }, function(v1, v) { v1.dcg_nodeKey = v.dcg_nodeKey; v1.width = v.width; v1.height = v.height; /* dagre does not seem to accept input positions if(v.dcg_nodeFixed) { v1.x = v.dcg_nodeFixed.x; v1.y = v.dcg_nodeFixed.y; } */ }, function(k, o) { _dagreGraph.setNode(k, o); }, function(k) { _dagreGraph.removeNode(k); }); var wedges = regenerate_objects(_edges, edges, null, function(e) { return e.dcg_edgeKey; }, function(e1, e) { e1.dcg_edgeKey = e.dcg_edgeKey; e1.dcg_edgeSource = e.dcg_edgeSource; e1.dcg_edgeTarget = e.dcg_edgeTarget; }, function(k, o, e) { _dagreGraph.setEdge(e.dcg_edgeSource, e.dcg_edgeTarget, o); }, function(k, e) { _dagreGraph.removeEdge(e.dcg_edgeSource, e.dcg_edgeTarget, e.dcg_edgeKey); }); clusters = clusters.filter(function(c) { return /^cluster/.test(c.dcg_clusterKey); }); clusters.forEach(function(c) { _dagreGraph.setNode(c.dcg_clusterKey, c); }); clusters.forEach(function(c) { if(c.dcg_clusterParent) _dagreGraph.setParent(c.dcg_clusterKey, c.dcg_clusterParent); }); nodes.forEach(function(n) { if(n.dcg_nodeParentCluster) _dagreGraph.setParent(n.dcg_nodeKey, n.dcg_nodeParentCluster); }); function dispatchState(event) { _dispatch[event]( wnodes, wedges.map(function(e) { return {dcg_edgeKey: e.dcg_edgeKey}; }), clusters.map(function(c) { var c = Object.assign({}, _dagreGraph.node(c.dcg_clusterKey)); c.bounds = { left: c.x - c.width/2, top: c.y - c.height/2, right: c.x + c.width/2, bottom: c.y + c.height/2 }; return c; }) ); } _tick = function() { dispatchState('tick'); }; _done = function() { dispatchState('end'); }; } function start(options) { _dispatch.start(); dagre.layout(_dagreGraph); _done(); } function stop() { } var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); return Object.assign(graphviz, { layoutAlgorithm: function() { return 'dagre'; }, layoutId: function() { return _layoutId; }, supportsWebworker: function() { return true; }, on: function(event, f) { if(arguments.length === 1) return _dispatch.on(event); _dispatch.on(event, f); return this; }, init: function(options) { this.optionNames().forEach(function(option) { options[option] = options[option] || this[option](); }.bind(this)); init(options); return this; }, data: function(graph, nodes, edges, clusters) { data(nodes, edges, clusters); }, start: function() { start(); }, stop: function() { stop(); }, optionNames: function() { return graphviz_keys; }, populateLayoutNode: function() {}, populateLayoutEdge: function() {} }); }; dc_graph.dagre_layout.scripts = ['d3.js', 'dagre.js'];