-
Notifications
You must be signed in to change notification settings - Fork 611
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #63 from bpodgursky/master
Helper file for rendering to d3 and corresponding demo files, v2
- Loading branch information
Showing
5 changed files
with
432 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
text { | ||
font-weight: 300; | ||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | ||
font-size: 14px; | ||
} | ||
|
||
rect { | ||
fill: #fff; | ||
} | ||
|
||
.node > rect { | ||
stroke-width: 3px; | ||
stroke: #333; | ||
fill: none; | ||
opacity: 0.5; | ||
} | ||
|
||
.edge rect { | ||
fill: #fff | ||
} | ||
|
||
.edge path { | ||
fill: none; | ||
stroke: #333; | ||
stroke-width: 1.5px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
|
||
/* | ||
* Render a pure graphviz definition to the svg specified by svg selector. | ||
*/ | ||
function renderDotToD3(graphDef, svgSelector) { | ||
var result = dagre.dot.toObjects(graphDef); | ||
renderDagreObjsToD3({nodes: result.nodes, edges: result.edges}, svgSelector); | ||
} | ||
|
||
/* | ||
* Render javascript edges and nodes to the svg. This method should be more | ||
* convenient when data was serialized as json and node definitions would be duplicated | ||
* if edges had direct references to nodes. See state-graph-js.html for example. | ||
*/ | ||
function renderJSObjsToD3(nodeData, edgeData, svgSelector) { | ||
|
||
var nodeRefs = []; | ||
var edgeRefs = []; | ||
|
||
edgeData.forEach(function(e){ | ||
edgeRefs.push({ | ||
source: nodeData[e.source], | ||
target: nodeData[e.target], | ||
label: e.label, | ||
id: e.id | ||
}); | ||
}); | ||
|
||
for (var nodeKey in nodeData) { | ||
if (nodeData.hasOwnProperty(nodeKey)) { | ||
nodeRefs.push(nodeData[nodeKey]); | ||
} | ||
} | ||
|
||
renderDagreObjsToD3({nodes: nodeRefs, edges: edgeRefs}, svgSelector); | ||
} | ||
|
||
/* | ||
* Render javscript objects to svg, as produced by dagre.dot. | ||
*/ | ||
function renderDagreObjsToD3(graphData, svgSelector) { | ||
|
||
var nodeData = graphData.nodes; | ||
var edgeData = graphData.edges; | ||
|
||
var svg = d3.select(svgSelector); | ||
|
||
if (svg.select("#arrowhead").empty()) { | ||
svg.append('svg:defs').append('svg:marker') | ||
.attr('id', 'arrowhead') | ||
.attr('viewBox', '0 0 10 10') | ||
.attr('refX', 8) | ||
.attr('refY', 5) | ||
.attr('markerUnits', 'strokewidth') | ||
.attr('markerWidth', 8) | ||
.attr('markerHeight', 5) | ||
.attr('orient', 'auto') | ||
.attr('style', 'fill: #333') | ||
.append('svg:path') | ||
.attr('d', 'M 0 0 L 10 5 L 0 10 z'); | ||
} | ||
|
||
svg.selectAll("g").remove(); | ||
|
||
var svgGroup = svg.append("g"); | ||
var nodes = svgGroup | ||
.selectAll("g .node") | ||
.data(nodeData); | ||
|
||
var nodeEnter = nodes | ||
.enter() | ||
.append("g") | ||
.attr("class", function (d) { | ||
if (d.nodeclass) { | ||
return "node " + d.nodeclass; | ||
} else { | ||
return "node"; | ||
} | ||
}) | ||
.attr("id", function (d) { | ||
return "node-" + d.id; | ||
}) | ||
.each(function (d) { | ||
d.nodePadding = 10; | ||
}); | ||
nodeEnter.append("rect"); | ||
addLabels(nodeEnter); | ||
nodes.exit().remove(); | ||
|
||
var edges = svgGroup | ||
.selectAll("g .edge") | ||
.data(edgeData); | ||
|
||
var edgeEnter = edges | ||
.enter() | ||
.append("g") | ||
.attr("class", "edge") | ||
.attr("id", function (d) { | ||
return "edge-" + d.id; | ||
}) | ||
.each(function (d) { | ||
d.nodePadding = 0; | ||
}); | ||
|
||
edgeEnter | ||
.append("path") | ||
.attr("marker-end", "url(#arrowhead)"); | ||
addLabels(edgeEnter); | ||
edges.exit().remove(); | ||
|
||
var labelGroup = svgGroup.selectAll("g.label"); | ||
|
||
var foLabel = labelGroup | ||
.selectAll(".htmllabel") | ||
// TODO find a better way to get the dimensions for foriegnObjects | ||
.attr("width", "100000"); | ||
|
||
foLabel | ||
.select("div") | ||
.html(function (d) { | ||
return d.label; | ||
}) | ||
.each(function (d) { | ||
d.width = this.clientWidth; | ||
d.height = this.clientHeight; | ||
d.nodePadding = 0; | ||
}); | ||
|
||
foLabel | ||
.attr("width", function (d) { | ||
return d.width; | ||
}) | ||
.attr("height", function (d) { | ||
return d.height; | ||
}); | ||
|
||
var textLabel = labelGroup | ||
.filter(function (d) { | ||
return d.label && d.label[0] !== "<"; | ||
}); | ||
|
||
textLabel | ||
.select("text") | ||
.attr("text-anchor", "left") | ||
.append("tspan") | ||
.attr("dy", "1em") | ||
.text(function (d) { | ||
return d.label; | ||
}); | ||
|
||
labelGroup | ||
.each(function (d) { | ||
var bbox = this.getBBox(); | ||
d.bbox = bbox; | ||
|
||
d.width = bbox.width + 2 * d.nodePadding; | ||
d.height = bbox.height + 2 * d.nodePadding; | ||
|
||
}); | ||
|
||
// Add zoom behavior to the SVG canvas | ||
svgGroup.attr("transform", "translate(5, 5)"); | ||
svg.call(d3.behavior.zoom().on("zoom", function redraw() { | ||
svgGroup.attr("transform", | ||
"translate(" + d3.event.translate + ")" | ||
+ " scale(" + d3.event.scale + ")"); | ||
})); | ||
|
||
// Run the actual layout | ||
dagre.layout() | ||
.nodes(nodeData) | ||
.edges(edgeData) | ||
.run(); | ||
|
||
nodes | ||
.attr("transform", function (d) { | ||
return "translate(" + d.dagre.x + "," + d.dagre.y + ")"; | ||
}) | ||
.selectAll("g.node rect") | ||
.attr("rx", 5) | ||
.attr("ry", 5) | ||
.attr("x", function (d) { | ||
return -(d.bbox.width / 2 + d.nodePadding); | ||
}) | ||
.attr("y", function (d) { | ||
return -(d.bbox.height / 2 + d.nodePadding); | ||
}) | ||
.attr("width", function (d) { | ||
return d.width; | ||
}) | ||
.attr("height", function (d) { | ||
return d.height; | ||
}); | ||
|
||
edges | ||
.selectAll("path") | ||
.attr("d", function (d) { | ||
var points = d.dagre.points.slice(0); | ||
points.unshift(dagre.util.intersectRect(d.source.dagre, points[0])); | ||
|
||
var preTarget = points[points.length - 2]; | ||
var target = dagre.util.intersectRect(d.target.dagre, points[points.length - 1]); | ||
|
||
// This shortens the line by a couple pixels so the arrowhead won't overshoot the edge of the target | ||
var deltaX = preTarget.x - target.x; | ||
var deltaY = preTarget.y - target.y; | ||
var m = 2 / Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); | ||
points.push({ | ||
x: target.x + m * deltaX, | ||
y: target.y + m * deltaY | ||
} | ||
); | ||
|
||
return d3.svg.line() | ||
.x(function (e) { | ||
return e.x; | ||
}) | ||
.y(function (e) { | ||
return e.y; | ||
}) | ||
.interpolate("bundle") | ||
.tension(.8) | ||
(points); | ||
}); | ||
|
||
svgGroup | ||
.selectAll("g.label rect") | ||
.attr("x", function (d) { | ||
return -d.nodePadding; | ||
}) | ||
.attr("y", function (d) { | ||
return -d.nodePadding; | ||
}) | ||
.attr("width", function (d) { | ||
return d.width; | ||
}) | ||
.attr("height", function (d) { | ||
return d.height; | ||
}); | ||
|
||
nodes | ||
.selectAll("g.label") | ||
.attr("transform", function (d) { | ||
return "translate(" + (-d.bbox.width / 2) + "," + (-d.bbox.height / 2) + ")"; | ||
}); | ||
|
||
edges | ||
.selectAll("g.label") | ||
.attr("transform", function (d) { | ||
var points = d.dagre.points; | ||
|
||
if (points.length > 1) { | ||
var x = (points[0].x + points[1].x) / 2; | ||
var y = (points[0].y + points[1].y) / 2; | ||
return "translate(" + (-d.bbox.width / 2 + x) + "," + (-d.bbox.height / 2 + y) + ")"; | ||
} else { | ||
return "translate(" + (-d.bbox.width / 2 + points[0].x) + "," + (-d.bbox.height / 2 + points[0].y) + ")"; | ||
} | ||
}); | ||
} | ||
|
||
function addLabels(selection) { | ||
|
||
var labelGroup = selection | ||
.append("g") | ||
.attr("class", "label"); | ||
labelGroup.append("rect"); | ||
|
||
var foLabel = labelGroup | ||
.filter(function (d) { | ||
return d.label && d.label[0] === "<"; | ||
}) | ||
.append("foreignObject") | ||
.attr("class", "htmllabel"); | ||
|
||
foLabel | ||
.append("xhtml:div") | ||
.style("float", "left"); | ||
|
||
labelGroup | ||
.filter(function (d) { | ||
return d.label && d.label[0] !== "<"; | ||
}) | ||
.append("text") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
|
||
<link rel="stylesheet" type="text/css" href="dagre-d3-simple.css"> | ||
|
||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | ||
<script type="text/javascript" src="../dagre.js"></script> | ||
<script src="dagre-d3-simple.js"></script> | ||
|
||
<div id="attach"> | ||
<svg id="svg-canvas" width=800 height=600></svg> | ||
</div> | ||
|
||
<style> | ||
.type-TK > .label > rect { | ||
fill: #00ffd0; | ||
} | ||
svg { | ||
border: 1px solid #999; | ||
} | ||
</style> | ||
|
||
<script> | ||
|
||
var nodes = { | ||
0: {"label": "TOP", "id": "0", "nodeclass": "type-TOP"}, | ||
1: {"label": "S", "id": "1", "nodeclass": "type-S"}, | ||
2: {"label": "NP", "id": "2", "nodeclass": "type-NP"}, | ||
3: {"label": "DT", "id": "3", "nodeclass": "t1ype-DT"}, | ||
4: {"label": "This", "id": "4", "nodeclass": "type-TK"}, | ||
5: {"label": "VP", "id": "5", "nodeclass": "type-VP"}, | ||
6: {"label": "VBZ", "id": "6", "nodeclass": "type-VBZ"}, | ||
7: {"label": "is", "id": "7", "nodeclass": "type-TK"}, | ||
8: {"label": "NP", "id": "8", "nodeclass": "type-NP"}, | ||
9: {"label": "DT", "id": "9", "nodeclass": "type-DT"}, | ||
10: {"label": "an", "id": "10", "nodeclass": "type-TK"}, | ||
11: {"label": "NN", "id": "11", "nodeclass": "type-NN"}, | ||
12: {"label": "example", "id": "12", "nodeclass": "type-TK"}, | ||
13: {"label": ".", "id": "13", "nodeclass": "type-."}, | ||
14: {"label": "sentence", "id": "14", "nodeclass": "type-TK"} | ||
}; | ||
|
||
var edges = | ||
[ | ||
{"source": 3, "target": 4, "id": "3-4"}, | ||
{"source": 2, "target": 3, "id": "2-3"}, | ||
{"source": 1, "target": 2, "id": "1-2"}, | ||
{"source": 6, "target": 7, "id": "6-7"}, | ||
{"source": 5, "target": 6, "id": "5-6"}, | ||
{"source": 9, "target": 10, "id": "9-10"}, | ||
{"source": 8, "target": 9, "id": "8-9"}, | ||
{"source": 11, "target": 12, "id": "11-12"}, | ||
{"source": 8, "target": 11, "id": "8-11"}, | ||
{"source": 5, "target": 8, "id": "5-8"}, | ||
{"source": 1, "target": 5, "id": "1-5"}, | ||
{"source": 13, "target": 14, "id": "13-14"}, | ||
{"source": 1, "target": 13, "id": "1-13"}, | ||
{"source": 0, "target": 1, "id": "0-1"} | ||
]; | ||
|
||
renderJSObjsToD3(nodes, edges, "svg"); | ||
|
||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<link rel="stylesheet" type="text/css" href="dagre-d3-simple.css"> | ||
|
||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | ||
<script type="text/javascript" src="../dagre.js"></script> | ||
<script src="dagre-d3-simple.js"></script> | ||
|
||
<svg id="svg-canvas" width=800 height=600></svg> | ||
|
||
<style> | ||
#node-E rect { | ||
fill: #acf; | ||
} | ||
svg { | ||
border: 1px solid #999; | ||
} | ||
</style> | ||
|
||
<script> | ||
renderDotToD3('digraph {A [label="<div style=\'padding: 10px;\'>A <span style=\'font-size:32px\'>Big</span> <span style=\'color:red;\'>HTML</span> Source!</div>"];C;E [label="A blue sink"];A -> B -> C;B -> D -> E;C -> E;A -> D [label="<div>A multi-rank <span style=\'color:blue;\'>HTML</span> edge!</div>"];}', "svg"); | ||
</script> |
Oops, something went wrong.