/**
* `dc_graph.graphviz_attrs defines a basic set of attributes which layout engines should
* implement - although these are not required, they make it easier for clients and
* modes (like expand_collapse) to work with multiple layout engines.
*
* these attributes are {@link http://www.graphviz.org/doc/info/attrs.html from graphviz}
* @class graphviz_attrs
* @memberof dc_graph
* @return {Object}
**/
dc_graph.graphviz_attrs = function() {
return {
/**
* Direction to draw ranks.
* @method rankdir
* @memberof dc_graph.graphviz_attrs
* @instance
* @param {String} [rankdir='TB'] 'TB', 'LR', 'BT', or 'RL'
**/
rankdir: property('TB'),
/**
* Spacing in between nodes in the same rank.
* @method nodesep
* @memberof dc_graph.graphviz_attrs
* @instance
* @param {String} [nodesep=40]
**/
nodesep: property(40),
/**
* Spacing in between ranks.
* @method ranksep
* @memberof dc_graph.graphviz_attrs
* @instance
* @param {String} [ranksep=40]
**/
ranksep: property(40)
};
};
// graphlib-dot seems to wrap nodes in an extra {value}
// actually this is quite a common problem with generic libs
function nvalue(n) {
return n.value.value ? n.value.value : n.value;
}
// apply standard accessors to a diagram in order to style it as graphviz would
// this is a work in progress
dc_graph.apply_graphviz_accessors = function(diagram) {
diagram
.nodeLabel(function(n) {
var label = nvalue(n).label;
if(label === undefined)
label = n.key;
return label && label.split(/\n|\\n/);
})
.nodeRadius(function(n) {
// should do width & height instead, #25
return nvalue(n).radius || 25;
})
.nodeShape(function(n) { return nvalue(n).shape; })
.nodeFill(function(n) { return nvalue(n).fillcolor || 'white'; })
.nodeOpacity(function(n) {
// not standard gv
return nvalue(n).opacity || 1;
})
.nodeLabelFill(function(n) { return nvalue(n).fontcolor || 'black'; })
.nodeTitle(function(n) {
return (nvalue(n).htmltip || nvalue(n).jsontip) ? null :
nvalue(n).tooltip !== undefined ?
nvalue(n).tooltip :
diagram.nodeLabel()(n);
})
.nodeStrokeWidth(function(n) {
// it is debatable whether a point === a pixel but they are close
// https://graphicdesign.stackexchange.com/questions/199/point-vs-pixel-what-is-the-difference
var penwidth = nvalue(n).penwidth;
return penwidth !== undefined ? +penwidth : 1;
})
.edgeLabel(function(e) { return e.value.label ? e.value.label.split(/\n|\\n/) : ''; })
.edgeStroke(function(e) { return e.value.color || 'black'; })
.edgeOpacity(function(e) {
// not standard gv
return e.value.opacity || 1;
})
.edgeArrowSize(function(e) {
return e.value.arrowsize || 1;
})
// need directedness to default these correctly, see #106
.edgeArrowhead(function(e) {
var head = e.value.arrowhead;
return head !== undefined ? head : 'vee';
})
.edgeArrowtail(function(e) {
var tail = e.value.arrowtail;
return tail !== undefined ? tail : null;
})
.edgeStrokeDashArray(function(e) {
switch(e.value.style) {
case 'dotted':
return [1,5];
}
return null;
});
var draw_clusters = diagram.child('draw-clusters');
if(draw_clusters) {
draw_clusters
.clusterStroke(function(c) {
return c.value.color || 'black';
})
.clusterFill(function(c) {
return c.value.style === 'filled' ? c.value.fillcolor || c.value.color || c.value.bgcolor : null;
})
.clusterLabel(function(c) {
return c.value.label;
});
}
};
dc_graph.snapshot_graphviz = function(diagram) {
var xDomain = diagram.x().domain(), yDomain = diagram.y().domain();
return {
nodes: diagram.nodeGroup().all().map(function(n) {
return diagram.getWholeNode(n.key);
})
.filter(function(x) { return x; })
.map(function(n) {
return {
key: diagram.nodeKey.eval(n),
label: diagram.nodeLabel.eval(n),
fillcolor: diagram.nodeFillScale()(diagram.nodeFill.eval(n)),
penwidth: diagram.nodeStrokeWidth.eval(n),
// not supported as input, see dc.graph.js#25
// width: n.cola.dcg_rx*2,
// height: n.cola.dcg_ry*2,
// not graphviz attributes
// until we have w/h
radius: diagram.nodeRadius.eval(n),
// does not seem to exist in gv
opacity: diagram.nodeOpacity.eval(n),
// should be pos
x: n.cola.x,
y: n.cola.y
};
}),
edges: diagram.edgeGroup().all().map(function(e) {
return diagram.getWholeEdge(e.key);
}).map(function(e) {
return {
key: diagram.edgeKey.eval(e),
source: diagram.edgeSource.eval(e),
target: diagram.edgeTarget.eval(e),
color: diagram.edgeStroke.eval(e),
arrowsize: diagram.edgeArrowSize.eval(e),
opacity: diagram.edgeOpacity.eval(e),
// should support dir, see dc.graph.js#106
arrowhead: diagram.edgeArrowhead.eval(e),
arrowtail: diagram.edgeArrowtail.eval(e)
};
}),
bounds: {
left: xDomain[0],
top: yDomain[0],
right: xDomain[1],
bottom: yDomain[1]
}
};
};