提交 476ad0e1 authored 作者: Christof Angermueller's avatar Christof Angermueller

Update d3print method to write dot and html file

上级 4d38e314
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#Table of Contents\n",
"* [Model](#Model)\n",
"* [Example 1](#Example-1)\n",
"* [Example 2](#Example-2)\n",
"* [Example 3](#Example-3)\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Couldn't import dot_parser, loading of dot files will not be possible.\n"
]
}
],
"source": [
"import numpy\n",
"import theano\n",
"import theano.tensor as T\n",
"import theano.printing as pr\n",
"import theano.d3printing as d3p\n",
"rng = numpy.random"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Model "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Training data\n",
"N = 400\n",
"feats = 784\n",
"D = (rng.randn(N, feats).astype(theano.config.floatX), rng.randint(size=N,low=0, high=2).astype(theano.config.floatX))\n",
"training_steps = 10000\n",
"\n",
"# Declare Theano symbolic variables\n",
"x = T.matrix(\"x\")\n",
"y = T.vector(\"y\")\n",
"w = theano.shared(rng.randn(feats).astype(theano.config.floatX), name=\"w\")\n",
"b = theano.shared(numpy.asarray(0., dtype=theano.config.floatX), name=\"b\")\n",
"x.tag.test_value = D[0]\n",
"y.tag.test_value = D[1]\n",
"\n",
"# Construct Theano expression graph\n",
"p_1 = 1 / (1 + T.exp(-T.dot(x, w)-b)) # Probability of having a one\n",
"prediction = p_1 > 0.5 # The prediction that is done: 0 or 1\n",
"\n",
"# Compute gradients\n",
"xent = -y*T.log(p_1) - (1-y)*T.log(1-p_1) # Cross-entropy\n",
"cost = xent.mean() + 0.01*(w**2).sum() # The cost to optimize\n",
"gw,gb = T.grad(cost, [w,b])\n",
"\n",
"# Training and prediction function\n",
"train = theano.function(inputs=[x,y], outputs=[prediction, xent], updates=[[w, w-0.01*gw], [b, b-0.01*gb]], name = \"train\")\n",
"predict = theano.function(inputs=[x], outputs=prediction, name = \"predict\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example 1 "
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The output file is available at p1.png\n",
"The output file is available at p1.html\n"
]
}
],
"source": [
"pr.pydotprint(p_1, outfile='p1.png', var_with_name_simple=True)\n",
"d3p.d3print(p_1, 'p1.html')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href='p1.html'><img src='p1.png'/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[open](./p1.html)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example 2"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The output file is available at predict.png\n",
"The output file is available at predict.html\n"
]
}
],
"source": [
"pr.pydotprint(predict, outfile='predict.png', var_with_name_simple=True)\n",
"d3p.d3print(predict, 'predict.html')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href='predict.html'><img src='predict.png'/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[open](./predict.html)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example 3"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The output file is available at train.png\n",
"The output file is available at train.html\n"
]
}
],
"source": [
"pr.pydotprint(train, outfile='train.png', var_with_name_simple=True)\n",
"d3p.d3print(train, 'train.html')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href='train.html'><img src='train.png'/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[open](./train.html)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
digraph G { graph [bb="0,0,719,672"]; "DimShuffle{x}" [height=0.5, pos="558,478", shape=ellipse, width=1.8374]; "Elemwise{sub,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="461,390", shape=ellipse, style=filled, width=3.0624]; "DimShuffle{x}" -> "Elemwise{sub,no_inplace}" [label="1 TensorType(float64, (True,))", lp="632.5,434", pos="e,506.93,406.57 553.52,459.62 549.99,448.82 544.12,435.29 535,426 529.47,420.37 522.85,415.53 515.89,411.41"]; "name=b TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="558,566", shape=box, style=filled, width=3.0625]; "name=b TensorType(float64, scalar)" -> "DimShuffle{x}" [label="TensorType(float64, scalar)", lp="636,522", pos="e,558,496.08 558,547.6 558,535.75 558,519.82 558,506.29"]; dot [height=0.5, pos="363,566", shape=ellipse, width=0.75]; "Elemwise{neg,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="363,478", shape=ellipse, style=filled, width=3.0624]; dot -> "Elemwise{neg,no_inplace}" [label="TensorType(float64, vector)", lp="442,522", pos="e,363,496.08 363,547.6 363,535.75 363,519.82 363,506.29"]; "name=x TensorType(float64, matrix)" [fillcolor=limegreen, height=0.5, pos="241,654", shape=box, style=filled, width=3.1181]; "name=x TensorType(float64, matrix)" -> dot [label="0 TensorType(float64, matrix)", lp="378,610", pos="e,340.56,576.07 255.77,636 265.61,625.35 279.21,611.84 293,602 304.81,593.57 318.95,586.09 331.42,580.22"]; "name=w TensorType(float64, vector)" [fillcolor=limegreen, height=0.5, pos="485,654", shape=box, style=filled, width=3.1389]; "name=w TensorType(float64, vector)" -> dot [label="1 TensorType(float64, vector)", lp="559.5,610", pos="e,389.41,570.28 481.35,635.78 478.2,624.78 472.59,610.94 463,602 445.54,585.73 420.08,576.96 399.23,572.27"]; "DimShuffle{x} id=2" [height=0.5, pos="247,302", shape=ellipse, width=2.3721]; "Elemwise{add,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="369,194", shape=ellipse, style=filled, width=3.0624]; "DimShuffle{x} id=2" -> "Elemwise{add,no_inplace}" [label="0 TensorType(int8, (True,))", lp="334,248", pos="e,290.21,206.6 244.34,283.76 242.92,267.96 243.54,244.85 256,230 262.8,221.89 271.45,215.63 280.91,210.8"]; "val=1 TensorType(int8, scalar)" [fillcolor=limegreen, height=0.5, pos="161,390", shape=box, style=filled, width=2.6389]; "val=1 TensorType(int8, scalar)" -> "DimShuffle{x} id=2" [label="TensorType(int8, scalar)", lp="305.5,346", pos="e,242.7,320.2 201.81,371.98 210.24,367.09 218.52,361.12 225,354 231.25,347.13 235.92,338.22 239.3,329.76"]; "DimShuffle{x} id=3" [height=0.5, pos="85,248", shape=ellipse, width=2.3721]; "val=1 TensorType(int8, scalar)" -> "DimShuffle{x} id=3" [label="TensorType(int8, scalar)", lp="151.5,346", pos="e,78.947,266.23 101.23,371.9 93.625,367.28 86.827,361.42 82,354 66.992,330.94 70.718,298.58 76.308,275.9"]; "Elemwise{true_div,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="164,106", shape=ellipse, style=filled, width=3.5561]; "DimShuffle{x} id=3" -> "Elemwise{true_div,no_inplace}" [label="0 TensorType(int8, (True,))", lp="172,194", pos="e,140.09,123.74 84.103,229.75 84.024,214.78 85.627,192.93 94,176 102.79,158.23 118.02,142.37 132.04,130.36"]; "Elemwise{neg,no_inplace}" -> "Elemwise{sub,no_inplace}" [label="0 TensorType(float64, vector)", lp="446.5,434", pos="e,389.83,403.99 357.87,459.89 355.74,449.21 355.15,435.69 362,426 366.99,418.94 373.53,413.29 380.88,408.78"]; "Elemwise{exp,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="461,302", shape=ellipse, style=filled, width=3.0624]; "Elemwise{sub,no_inplace}" -> "Elemwise{exp,no_inplace}" [label="TensorType(float64, vector)", lp="540,346", pos="e,461,320.08 461,371.6 461,359.75 461,343.82 461,330.29"]; "Elemwise{exp,no_inplace}" -> "Elemwise{add,no_inplace}" [label="1 TensorType(float64, vector)", lp="526.5,248", pos="e,394.8,211.6 452.54,284.02 444.46,268.82 431.28,246.48 416,230 412.01,225.7 407.43,221.56 402.74,217.73"]; "Elemwise{add,no_inplace}" -> "Elemwise{true_div,no_inplace}" [label="1 TensorType(float64, vector)", lp="369.5,150", pos="e,202.57,123.18 330.92,177.03 297.33,162.93 248.22,142.33 211.96,127.12"]; "TensorType(float64, vector) id=12" [fillcolor=dodgerblue, height=0.5, pos="164,18", shape=box, style=filled, width=2.9236]; "Elemwise{true_div,no_inplace}" -> "TensorType(float64, vector) id=12" [label="TensorType(float64, vector)", lp="243,62", pos="e,164,36.084 164,87.597 164,75.746 164,59.817 164,46.292"]; }
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type='text/javascript' src="http://cpettitt.github.io/project/dagre-d3/v0.1.5/dagre-d3.min.js"></script>
<script type='text/javascript' src="http://cpettitt.github.io/project/graphlib-dot/v0.4.10/graphlib-dot.min.js"></script>
</head>
<body>
<style>
svg {
margin-left:auto;
margin-right:auto;
display:block;
position: fixed;
border: 0px solid black;
top:5%; left:0%; right:0% bottom=10%
}
.nodeRect {
stroke: black;
border: 3px solid black;
}
.nodeEllipse {
stroke: black;
border: 3px solid black;
}
.nodeText {
color: black;
}
.edge {
stroke-width: 3px;
cursor: pointer;
opacity: 0.4;
}
.edgeLabelRect {
stroke: black;
border: 1px solid black;
fill: skyblue;
opacity: 0.9;
}
.edgeLabelText {
fill: black;
text-anchor: start;
}
.arrowHead {
stroke: green;
stroke-width: 1px;
}
.arrowHead_n {
stroke: green;
}
.arrowHead_r {
stroke-width: 3px;
fill: red;
stroke: red;
}
.arrowHead_b {
stroke: dodgerblue;
}
.tooltip {
position: absolute;
text-align: center;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
</style>
<div>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
var path='p1.dot';
// Global attributes
var pad = 10;
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
var edgeDiv = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
function textSize(text, attr) {
var t = svg.append('text').text(text);
if (typeof(attr) != 'undefined') {
for (a in attr) {
t.attr(a, attr[a]);
}
}
var bbox = t.node().getBBox();
t.remove();
return bbox;
}
function exists(x) {
return typeof(x) != 'undefined';
}
var dotGraph;
var graph = {};
var nodes = [];
var edges = [];
var layout;
var scaleDotX;
var scaleDotY;
d3.text(path, function(data) {
dotGraph = graphlibDot.parse(data);
// Calculate width and height
var posMax = [0, 0];
for (var nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.value.pos = node.value.pos.split(',').map(function(d) {return parseInt(d);});
node.value.width = parseInt(node.value.width);
node.value.height = parseInt(node.value.height);
posMax[0] = Math.max(posMax[0], node.value.pos[0] + node.value.width);
posMax[1] = Math.max(posMax[1], node.value.pos[1] + node.value.height);
}
dotWidth = posMax[0];
dotHeight = posMax[1];
//svg.attr('viewBox', '0,0,' + dotWidth + ',' + dotHeight);
scaleDotX = d3.scale.linear().domain([0, dotWidth]).range([20, dotWidth * 1.2]);
scaleDotY = d3.scale.linear().domain([0, dotHeight]).range([20, dotHeight * 1.2]);
// Parse nodes
var i = 0;
for (nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.index = i++;
// x, y is center of node (not corner)
node.x = scaleDotX(node.value.pos[0]);
node.y = scaleDotY(dotHeight - (node.value.pos[1] + node.value.height));
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
node.fixed = true;
nodes.push(node);
dotGraph._nodes[nodeId] = node;
}
// Parse edges
for (edgeId in dotGraph._edges) {
var edge = dotGraph._edges[edgeId];
edge.source = dotGraph._nodes[edge.u].index;
edge.target = dotGraph._nodes[edge.v].index;
var size = textSize(edge.value.label, {'class': 'edgeLabelText'});
edge.value.width = size.width + 2 * pad;
edge.value.height = size.height + 2 * pad;
if (!exists(edge.value.color)) {
edge.value.color = 'black';
}
switch (edge.value.color) {
case 'dodgerblue':
edge.value.type = 'b';
break;
case 'red':
edge.value.type = 'r';
break;
default:
edge.value.type = 'n';
}
edges.push(edge);
dotGraph._edges[edgeId] = edge;
}
// Setup graph
graph['nodes'] = nodes;
graph['edges'] = edges;
var isEdgeOver = false;
var isEdgeLabelOver = false;
// Add edges
edges = pane.append('g').attr('id', 'edges').selectAll('path').data(graph['edges']).enter().append('path')
.attr('class', 'edge')
.attr('stroke', function(d) {return d.value.color;})
.attr('marker-mid', function(d) { return 'url(#edgeArrow_' + d.value.type + ')';})
.on('mouseover', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 1.0);
edgeDiv.transition()
.duration(200)
.style('opacity', .9);
edgeDiv
.html(d.value.label)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
})
.on('mouseout', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 0.4);
edgeDiv.transition()
.duration(200)
.style('opacity', 0);
});
// Add nodes
nodes = pane.append('g').attr('id', 'nodes').selectAll('g').data(graph['nodes']).enter().append('g');
function fillColor(f) {
return typeof(f) == 'undefined' ? 'white' : f;
}
function setNodeSize(node) {
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
}
function updateNode(d, node) {
var shape;
if (d.value.shape == 'ellipse') {
node.selectAll('ellipse').remove();
shape = node.append('ellipse')
.attr('class', 'nodeEllipse')
.attr('cx', d.value.cx)
.attr('cy', d.value.cy)
.attr('rx', d.value.width * 0.6)
.attr('ry', d.value.height * 0.6);
} else {
node.selectAll('rect').remove();
shape = node.append('rect')
.attr('class', 'nodeRect')
.attr('width', d.value.width)
.attr('height', d.value.height);
}
shape.attr('fill', fillColor(d.value.fillcolor));
node.selectAll('text').remove();
var text = node.append('text')
.attr('class', 'nodeText')
.attr('x', pad)
.attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;})
.on('dblclick', releaseNode);
}
nodes.each(function(d) {var node = d3.select(this); updateNode(d, node);});
// TODO: activate again without interfering with click event
//nodes.on('dblclick', releaseNode);
nodes.on('click', function(d) {
var pos = this.getBBox();
var node = d3.select(this);
if (d3.event.defaultPrevented) return;
var form = node.append('foreignObject')
.attr('x', pos.x)
.attr('y', pos.y)
.attr('width', d.value.width)
.attr('height', 25);
var input = form.append('xhtml:form').append('input')
.attr('style', 'width: ' + d.value.width + 'px')
.attr('value', function() {
this.focus();
return d.value.label;
})
.on('blur', function() {
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
})
.on('keypress', function() {
if (!d3.event) {
d3.event = window.event;
}
var event = d3.event;
if (event.keyCode == 13) {
if (typeof(event.cancelBubble)) {
event.cancelBubble = true;
}
if (event.stopPropagation) {
event.stopPropagation();
}
event.preventDefault();
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check thi
}
});
});
nodes.on('mouseover', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source == node || d.target == node) {
edge.transition()
.duration(200)
.style('opacity', 1.0);
}
});
});
nodes.on('mouseout', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source.index == node.index || d.target.index == node.index) {
edge.transition()
.duration(200)
.style('opacity', 0.4);
}
});
});
// Zoom and translate event handler
function zoom(d) {
pane.attr('transform', 'translate(' + d3.event.translate + ') scale(' + d3.event.scale + ')');
}
// Force layout
layout = d3.layout.force()
.nodes(graph['nodes'])
.links(graph['edges'])
.size([dotWidth, dotHeight])
.charge(-3000)
.linkDistance(50)
.linkStrength(0.1)
.on('tick', updateGraph);
// Drag behavour
var drag = layout.drag()
.on('dragstart', function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
d.fixed = true;
});
nodes.call(drag);
// Zoom behaviour
var bZoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
//.on("dblclick.zoom", null)
.on('zoom', zoom);
svg.call(bZoom);
svg.on("dblclick.zoom", null);
// Start force layout
layout.start();
});
function length(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}
function pathPos(x1, y1, x2, y2, c) {
x = (1 - c) * x1 + c * x2;
y = (1 - c) * y1 + c * y2;
p = x + ',' + y;
return p;
}
// Update graph
function updateGraph() {
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; });
// Update edges
edges.attr('d', function(d) {
var dist = 100;
var l = length(d.source.x, d.source.y, d.target.x, d.target.y);
var n = Math.max(2, Math.floor(l / dist));
var marker = [];
for (var i = 1; i < n; ++i) {
marker.push(i / n);
}
var markerPos = marker.map(function(c) {return pathPos(d.source.x, d.source.y, d.target.x, d.target.y, c);});
var markerPos = ' L' + markerPos.join(' L');
return 'M' + d.source.x + ',' + d.source.y + markerPos + ' L' + d.target.x + ',' + d.target.y;
});
}
function releaseNode(d) {
d.fixed = false;
layout.start();
}
function releaseNodes() {
graph['nodes'].forEach (function (d) {
d.fixed = false;
});
layout.start();
}
function resetNodes() {
layout.stop();
var nodes = graph['nodes'];
nodes.forEach(function (node, i){
nodes[i].x = scaleDotX(node.value.pos[0]);
nodes[i].y = scaleDotY(dotHeight - (node.value.pos[1] + node.value.height));
nodes[i].px = nodes[i].x;
nodes[i].py = nodes[i].y;
nodes[i].fixed = true;
});
updateGraph();
layout.start();
}
</script>
</body>
</html>
digraph G { graph [bb="0,0,3064,868"]; "InplaceDimShuffle{x}" [height=0.5, pos="508,546", shape=ellipse, width=2.5686]; "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" [fillcolor="#FFAABB", height=0.5, pos="1180,458", shape=ellipse, style=filled, width=4.6165]; "InplaceDimShuffle{x}" -> "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" [label="1 TensorType(float64, (True,))", lp="991.5,502", pos="e,1077.2,472.16 583.13,535.39 700.83,520.32 929.42,491.07 1067,473.46"]; "name=b TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="199,654", shape=box, style=filled, width=3.0625]; "name=b TensorType(float64, scalar)" -> "InplaceDimShuffle{x}" [color=dodgerblue, label="TensorType(float64, scalar)", lp="478,600", pos="e,464.47,561.93 248.65,635.97 305.09,616.61 396.89,585.12 454.78,565.26"]; "Elemwise{Composite{(i0 - (i1 * i2))}}[(0, 0)]" [fillcolor="#FFAABB", height=0.5, pos="179,18", shape=ellipse, style=filled, width=4.9776]; "name=b TensorType(float64, scalar)" -> "Elemwise{Composite{(i0 - (i1 * i2))}}[(0, 0)]" [color=red, label="0 TensorType(float64, scalar)", lp="131.5,326", pos="e,115.8,34.997 136.27,635.88 94.787,620.32 48,592.64 48,547 48,547 48,547 48,105 48,72.701 76.173,51.864 106.39,38.804"]; "Shape_i{0}" [fillcolor=cyan, height=0.5, pos="2254,762", shape=ellipse, style=filled, width=1.4763]; "AllocEmpty{dtype='float64'}" [fillcolor="#FFAA22", height=0.5, pos="2189,654", shape=ellipse, style=filled, width=3.2589]; "Shape_i{0}" -> "AllocEmpty{dtype='float64'}" [label="TensorType(int64, scalar)", lp="2248,708", pos="e,2179.1,672.11 2210,751.54 2196.5,746.34 2183,738.32 2175,726 2166.4,712.82 2169.6,695.57 2175,681.36"]; "Elemwise{eq,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="2151,458", shape=ellipse, style=filled, width=2.9297]; "Shape_i{0}" -> "Elemwise{eq,no_inplace}" [label="1 TensorType(int64, scalar)", lp="2396,600", pos="e,2182.2,475.28 2292.5,749.53 2303.6,744.23 2314.5,736.66 2321,726 2337.7,698.44 2320.8,609.52 2304,582 2276.5,536.93 2226.3,501.37 \ 2190.9,480.32"]; "name=x TensorType(float64, matrix)" [fillcolor=limegreen, height=0.5, pos="2254,850", shape=box, style=filled, width=3.1181]; "name=x TensorType(float64, matrix)" -> "Shape_i{0}" [label="TensorType(float64, matrix)", lp="2334,806", pos="e,2254,780.08 2254,831.6 2254,819.75 2254,803.82 2254,790.29"]; "InplaceDimShuffle{1,0}" [height=0.5, pos="2630,708", shape=ellipse, width=2.779]; "name=x TensorType(float64, matrix)" -> "InplaceDimShuffle{1,0}" [color=dodgerblue, label="TensorType(float64, matrix)", lp="2537,806", pos="e,2602.5,725.41 2353.5,831.92 2375.1,827.08 2397.5,821.13 2418,814 2482.4,791.59 2552.4,754.11 2593.7,730.52"]; "CGemv{inplace}" [height=0.5, pos="1944,546", shape=ellipse, width=2.0569]; "name=x TensorType(float64, matrix)" -> "CGemv{inplace}" [label="2 TensorType(float64, matrix)", lp="1754,708", pos="e,1876.1,553.37 2141.5,835.48 1970.2,812.32 1656.8,758.98 1590,672 1580.3,659.31 1580.4,648.84 1590,636 1606.6,613.66 1623.9,627.8 \ 1650,618 1686.1,604.42 1692.1,593.2 1729,582 1773.5,568.51 1824.8,559.91 1866.1,554.61"]; "name=x.T TensorType(float64, matrix)" [height=0.5, pos="2876,600", width=4.2783]; "InplaceDimShuffle{1,0}" -> "name=x.T TensorType(float64, matrix)" [label="TensorType(float64, matrix)", lp="2877,654", pos="e,2840.7,617.58 2670.9,691.4 2686.1,685.49 2703.4,678.59 2719,672 2757.2,655.86 2800.1,636.37 2831.4,621.9"]; "CGemv{inplace} id=19" [height=0.5, pos="2157,106", shape=ellipse, width=2.7013]; "name=x.T TensorType(float64, matrix)" -> "CGemv{inplace} id=19" [label="2 TensorType(float64, matrix)", lp="2979,370", pos="e,2253.7,108.43 2881.2,581.84 2886.5,562.77 2894,530.98 2894,503 2894,503 2894,503 2894,193 2894,130.33 2468.2,113.25 2263.9,108.65"]; "Shape_i{0} id=3" [fillcolor=cyan, height=0.5, pos="1571,546", shape=ellipse, style=filled, width=2.002]; "Shape_i{0} id=3" -> "Elemwise{eq,no_inplace}" [label="0 TensorType(int64, scalar)", lp="1844,502", pos="e,2046.9,461.21 1625.2,534.09 1654.2,527.86 1690.3,519.41 1722,510 1741.9,504.08 1745.7,498.54 1766,494 1855.6,473.98 1959.5,465.36 \ 2036.7,461.68"]; "InplaceDimShuffle{x} id=7" [height=0.5, pos="1659,458", shape=ellipse, width=3.1172]; "Shape_i{0} id=3" -> "InplaceDimShuffle{x} id=7" [color=dodgerblue, label="TensorType(int64, scalar)", lp="1645,502", pos="e,1597.3,473.16 1566.9,527.95 1565.3,517.28 1565.3,503.77 1572,494 1576.3,487.74 1582,482.59 1588.3,478.37"]; "name=y TensorType(float64, vector)" [fillcolor=limegreen, height=0.5, pos="1081,654", shape=box, style=filled, width=3.0903]; "name=y TensorType(float64, vector)" -> "Shape_i{0} id=3" [label="TensorType(float64, vector)", lp="1478,600", pos="e,1518.5,558.36 1159.7,635.97 1256.8,614.98 1419.7,579.74 1508.5,560.51"]; "Elemwise{sub,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="519,370", shape=ellipse, style=filled, width=3.0624]; "name=y TensorType(float64, vector)" -> "Elemwise{sub,no_inplace}" [label="1 TensorType(float64, vector)", lp="443.5,502", pos="e,471.35,386.34 969.54,651.53 773.46,648.4 385.48,639.5 365,618 333.32,584.73 310.57,554.44 395,440 411.59,417.51 437.83,401.4 461.9,\ 390.44"]; "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [fillcolor="#FFAABB", height=0.5, pos="1579,194", shape=ellipse, style=filled, width=7.3955]; "name=y TensorType(float64, vector)" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="3 TensorType(float64, vector)", lp="2062.5,414", pos="e,1809.4,203.06 1192.5,650.77 1312.4,647.33 1494.8,638.79 1561,618 1655.4,588.35 1659.2,541.57 1746,494 1842.7,441 1874,442.62 1978,\ 406 1980.2,405.21 2302.6,301.93 2304,300 2366.8,213.53 2226.9,237.1 2185,230 2116.5,218.37 1957.6,209.44 1819.7,203.5"]; "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [fillcolor="#FFAABB", height=0.5, pos="964,282", shape=ellipse, style=filled, width=7.3087]; "name=y TensorType(float64, vector)" -> "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [label="0 TensorType(float64, vector)", lp="815.5,458", pos="e,805.95,296.4 969.3,640.75 866.87,626.32 731,597.63 731,547 731,547 731,547 731,369 731,345.49 727.5,333.69 745,318 754.35,309.62 \ 773.13,303.25 795.95,298.39"]; "Elemwise{sub,no_inplace}" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="5 TensorType(float64, vector)", lp="607.5,282", pos="e,1343.6,202.42 513.52,351.82 507.28,328.56 500.62,287.28 523,264 550.73,235.15 1030.1,213.6 1333.3,202.78"]; "Elemwise{sub,no_inplace}" -> "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [label="3 TensorType(float64, vector)", lp="632.5,326", pos="e,778.18,294.77 524.07,351.81 528.44,340.21 535.99,325.68 548,318 557.92,311.66 664.92,302.74 767.9,295.49"]; "val=[ 1.] TensorType(float64, (True,))" [fillcolor=limegreen, height=0.5, pos="519,458", shape=box, style=filled, width=3.1944]; "val=[ 1.] TensorType(float64, (True,))" -> "Elemwise{sub,no_inplace}" [label="0 TensorType(float64, (True,))", lp="605.5,414", pos="e,519,388.08 519,439.6 519,427.75 519,411.82 519,398.29"]; "AllocEmpty{dtype='float64'}" -> "CGemv{inplace}" [color=red, label="0 TensorType(float64, vector)", lp="2219.5,600", pos="e,2016.8,549.67 2158.7,636.57 2150.5,631.29 2141.9,624.97 2135,618 2121.6,604.53 2127.6,592.78 2112,582 2087.2,564.9 2055.8,555.85 \ 2026.9,551.15"]; "Assert{msg='Theano Assert failed!'}" [height=0.5, pos="2151,282", shape=ellipse, width=4.013]; "Elemwise{eq,no_inplace}" -> "Assert{msg='Theano Assert failed!'}" [label="1 TensorType(int8, scalar)", lp="2225.5,370", pos="e,2151,300.07 2151,440 2151,409.85 2151,346.79 2151,310.43"]; "Elemwise{Cast{float64}}" [fillcolor="#FFAABB", height=0.5, pos="1747,370", shape=ellipse, style=filled, width=2.9207]; "InplaceDimShuffle{x} id=7" -> "Elemwise{Cast{float64}}" [label="TensorType(int64, (True,))", lp="1812.5,414", pos="e,1742.7,388.24 1699.4,441.12 1708.7,436.06 1717.9,429.72 1725,422 1731.3,415.17 1736,406.27 1739.4,397.81"]; "CGemv{inplace}" -> "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" [color=red, label="0 TensorType(float64, vector)", lp="2020.5,502", pos="e,1328.9,466.09 1941.4,527.81 1938.6,516.21 1933.1,501.68 1922,494 1886.9,469.7 1580.7,478.01 1538,476 1472.9,472.93 1401.2,469.53 \ 1339.2,466.58"]; "val=1.0 TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="1708,654", shape=box, style=filled, width=3.0278]; "val=1.0 TensorType(float64, scalar)" -> "CGemv{inplace}" [label="1 TensorType(float64, scalar)", lp="1856.5,600", pos="e,1873.1,551.52 1719,635.79 1730.4,619.32 1749.9,595.1 1773,582 1800.2,566.56 1833.3,557.88 1863.1,553.02"]; "name=w TensorType(float64, vector)" [fillcolor=limegreen, height=0.5, pos="2597,654", shape=box, style=filled, width=3.1389]; "name=w TensorType(float64, vector)" -> "CGemv{inplace}" [label="3 TensorType(float64, vector)", lp="2590.5,600", pos="e,2017.7,548.11 2536.3,635.87 2525.5,631.16 2514.9,625.28 2506,618 2491.3,605.99 2499.3,591.66 2483,582 2444.9,559.49 2169.9,551.15 \ 2028.1,548.32"]; "name=w TensorType(float64, vector)" -> "CGemv{inplace} id=19" [color=red, label="0 TensorType(float64, vector)", lp="2773.5,370", pos="e,2247.8,112.55 2653.2,635.79 2661.5,631.15 2669.2,625.32 2675,618 2695,592.83 2689,579.16 2689,547 2689,547 2689,547 2689,193 2689,\ 149.8 2412.1,124.17 2257.8,113.24"]; "val=0.0 TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="1944,654", shape=box, style=filled, width=3.0278]; "val=0.0 TensorType(float64, scalar)" -> "CGemv{inplace}" [label="4 TensorType(float64, scalar)", lp="2027.5,600", pos="e,1944,564.34 1944,635.97 1944,619.38 1944,593.88 1944,574.43"]; "Elemwise{inv,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="1879,282", shape=ellipse, style=filled, width=3.0304]; "Elemwise{Cast{float64}}" -> "Elemwise{inv,no_inplace}" [label="TensorType(float64, (True,))", lp="1836.5,326", pos="e,1793.6,293.26 1745.3,351.9 1745.2,340.94 1746.9,327.12 1755,318 1763,308.98 1773.1,302.21 1784.1,297.13"]; "Elemwise{Cast{float64}}" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="6 TensorType(float64, (True,))", lp="1674.5,282", pos="e,1574.7,212.06 1736.9,351.97 1729.1,340.46 1717.3,325.95 1703,318 1657.8,292.88 1623.3,337.83 1588,300 1568.8,279.46 1569.4,245.81 \ 1573,222.14"]; sigmoid [fillcolor="#FFAABB", height=0.5, pos="1925,370", shape=ellipse, style=filled, width=1.1152]; "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" -> sigmoid [label="TensorType(float64, vector)", lp="1642,414", pos="e,1892.7,380.92 1334.4,451.28 1415.9,446.46 1506.6,437.71 1544,422 1554.2,417.72 1552.7,410.1 1563,406 1624.6,381.39 1795.5,398.57 \ 1861,388 1868.2,386.84 1875.7,385.22 1883,383.43"]; "Elemwise{neg,no_inplace}" [fillcolor="#FFAABB", height=0.5, pos="913,370", shape=ellipse, style=filled, width=3.0624]; "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" -> "Elemwise{neg,no_inplace}" [label="TensorType(float64, vector)", lp="993,414", pos="e,908.16,388.2 1019.9,453.04 972.64,448.31 929.37,439.18 914,422 908.29,415.62 906.75,406.78 907.05,398.26"]; "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="4 TensorType(float64, vector)", lp="1614.5,326", pos="e,1571.2,212.21 1301.4,445.69 1384.1,435.49 1483.9,417.46 1514,388 1536.8,365.67 1520.5,348.47 1530,318 1540.6,284.05 1556.3,246.22 \ 1567,221.59"]; "Elemwise{Composite{((-i0) - i1)}}[(0, 0)]" -> "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [color=red, label="1 TensorType(float64, vector)", lp="1179.5,370", pos="e,1074.2,298.37 1147.5,440.33 1128.4,428.56 1105.9,410.82 1095,388 1088.1,373.55 1088.1,366.46 1095,352 1100.4,340.58 1111.6,345.42 \ 1117,334 1120,327.57 1121.4,323.56 1117,318 1111.6,311.21 1099.4,305.63 1084.1,301.08"]; "Elemwise{inv,no_inplace}" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="2 TensorType(float64, (True,))", lp="1842.5,238", pos="e,1636.9,211.61 1827.1,266.12 1777.2,251.82 1701.5,230.13 1646.9,214.46"]; sigmoid -> "Assert{msg='Theano Assert failed!'}" [color=dodgerblue, label="0 TensorType(float64, vector)", lp="2026.5,326", pos="e,2051.2,295.04 1926.3,351.82 1928,340.54 1932.2,326.37 1942,318 1950.2,310.97 1995,303.01 2041,296.46"]; "Elemwise{Composite{GT(scalar_sigmoid(i0), i1)}}" [fillcolor="#FFAABB", height=0.5, pos="277,282", shape=ellipse, style=filled, width=5.59]; "Elemwise{neg,no_inplace}" -> "Elemwise{Composite{GT(scalar_sigmoid(i0), i1)}}" [label="0 TensorType(float64, vector)", lp="845.5,326", pos="e,422.14,294.49 845.06,355.7 818.58,349.91 788.13,342.5 761,334 742.73,328.28 739.65,322.36 721,318 621.58,294.78 593.78,308.02 \ 492,300 472.68,298.48 452.39,296.87 432.3,295.29"]; "Elemwise{neg,no_inplace}" -> "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [label="4 TensorType(float64, vector)", lp="1028.5,326", pos="e,954.04,300.02 923.28,351.93 926.7,346.23 930.53,339.85 934,334 938.88,325.79 944.17,316.81 948.94,308.71"]; "Assert{msg='Theano Assert failed!'}" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [color=red, label="0 TensorType(float64, vector)", lp="2096.5,238", pos="e,1753.4,207.65 2093.6,265.39 2049.9,254.04 1988,239.12 1933,230 1878.2,220.93 1818.3,213.91 1763.5,208.62"]; "TensorType(int8, vector)" [fillcolor=dodgerblue, height=0.5, pos="488,194", shape=box, style=filled, width=2.1736]; "Elemwise{Composite{GT(scalar_sigmoid(i0), i1)}}" -> "TensorType(int8, vector)" [label="TensorType(int8, vector)", lp="429.5,238", pos="e,410.68,212.04 300.38,263.89 316.26,252.93 338.1,239.1 359,230 372.22,224.24 386.63,219.22 400.87,214.91"]; "val=[ 0.5] TensorType(float32, (True,))" [fillcolor=limegreen, height=0.5, pos="272,370", shape=box, style=filled, width=3.2847]; "val=[ 0.5] TensorType(float32, (True,))" -> "Elemwise{Composite{GT(scalar_sigmoid(i0), i1)}}" [label="1 TensorType(float32, (True,))", lp="361.5,326", pos="e,276.01,300.08 273.01,351.6 273.7,339.75 274.63,323.82 275.41,310.29"]; "Sum{acc_dtype=float64}" [height=0.5, pos="179,106", shape=ellipse, width=2.8658]; "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." -> "Sum{acc_dtype=float64}" [label="TensorType(float64, vector)", lp="259,150", pos="e,173.93,124.01 1351.4,184.61 1265.7,181.63 1167.4,178.41 1078,176 1053.1,175.33 197.64,175.65 180,158 173.79,151.79 172.24,142.73 \ 172.68,133.97"]; "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." -> "CGemv{inplace} id=19" [label="3 TensorType(float64, vector)", lp="2004.5,150", pos="e,2083.8,117.89 1683.7,177.43 1795.6,160.77 1970.8,134.7 2073.7,119.39"]; "val=[-1.] TensorType(float64, (True,))" [fillcolor=limegreen, height=0.5, pos="1389,370", shape=box, style=filled, width=3.2222]; "val=[-1.] TensorType(float64, (True,))" -> "Elemwise{Composite{((i0 * i1 * i2 * i3) + ((scalar_sigmoid((-i4)) *..." [label="1 TensorType(float64, (True,))", lp="1432.5,282", pos="e,1485.7,210.92 1371.9,351.66 1352.4,329.66 1325.6,291.3 1346,264 1362.2,242.24 1421.1,224.92 1475.6,213.07"]; "val=[-1.] TensorType(float64, (True,))" -> "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." [label="2 TensorType(float64, (True,))", lp="1260.5,326", pos="e,1085.4,297.98 1272.9,352.33 1227.5,345.39 1184,338.03 1174,334 1162.5,329.33 1162.5,322.81 1151,318 1133.5,310.67 1114.4,304.87 \ 1095.4,300.27"]; "TensorType(float64, vector) id=26" [fillcolor=dodgerblue, height=0.5, pos="964,194", shape=box, style=filled, width=2.9236]; "Elemwise{Composite{((i0 * scalar_softplus(i1)) - (i2 * i3 * scalar_..." -> "TensorType(float64, vector) id=26" [label="TensorType(float64, vector)", lp="1043,238", pos="e,964,212.08 964,263.6 964,251.75 964,235.82 964,222.29"]; "Sum{acc_dtype=float64}" -> "Elemwise{Composite{(i0 - (i1 * i2))}}[(0, 0)]" [label="2 TensorType(float64, scalar)", lp="262.5,62", pos="e,179,36.084 179,87.597 179,75.746 179,59.817 179,46.292"]; "val=-0.01 TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="276,194", shape=box, style=filled, width=3.1944]; "val=-0.01 TensorType(float64, scalar)" -> "CGemv{inplace} id=19" [label="1 TensorType(float64, scalar)", lp="965.5,150", pos="e,2059.6,107.51 391.14,176.83 394.12,176.53 397.08,176.26 400,176 1023,121.19 1774.1,109.92 2049.3,107.6"]; "val=0.9998 TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="2157,194", shape=box, style=filled, width=3.3125]; "val=0.9998 TensorType(float64, scalar)" -> "CGemv{inplace} id=19" [label="4 TensorType(float64, scalar)", lp="2240.5,150", pos="e,2157,124.08 2157,175.6 2157,163.75 2157,147.82 2157,134.29"]; "val=0.01 TensorType(float64, scalar)" [fillcolor=limegreen, height=0.5, pos="413,106", shape=box, style=filled, width=3.1181]; "val=0.01 TensorType(float64, scalar)" -> "Elemwise{Composite{(i0 - (i1 * i2))}}[(0, 0)]" [label="1 TensorType(float64, scalar)", lp="458.5,62", pos="e,285.55,32.604 396.38,87.768 384.5,76.465 367.56,62.293 350,54 332.97,45.956 314.12,39.755 295.35,34.975"]; }
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type='text/javascript' src="http://cpettitt.github.io/project/dagre-d3/v0.1.5/dagre-d3.min.js"></script>
<script type='text/javascript' src="http://cpettitt.github.io/project/graphlib-dot/v0.4.10/graphlib-dot.min.js"></script>
</head>
<body>
<style>
svg {
margin-left:auto;
margin-right:auto;
display:block;
position: fixed;
border: 0px solid black;
top:5%; left:0%; right:0% bottom=10%
}
.nodeRect {
stroke: black;
border: 3px solid black;
}
.nodeEllipse {
stroke: black;
border: 3px solid black;
}
.nodeText {
color: black;
}
.edge {
stroke-width: 3px;
cursor: pointer;
opacity: 0.4;
}
.edgeLabelRect {
stroke: black;
border: 1px solid black;
fill: skyblue;
opacity: 0.9;
}
.edgeLabelText {
fill: black;
text-anchor: start;
}
.arrowHead {
stroke: green;
stroke-width: 1px;
}
.arrowHead_n {
stroke: green;
}
.arrowHead_r {
stroke-width: 3px;
fill: red;
stroke: red;
}
.arrowHead_b {
stroke: dodgerblue;
}
.tooltip {
position: absolute;
text-align: center;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
}
</style>
<div>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript">
var path='train.dot';
// Global attributes
var pad = 10;
d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg')
.attr('width', '100%')
.attr('height', '95%');
var pane = svg.append('g');
var edgeDiv = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0.0);
// Definition head of edges
var markerData = [
{'id': 'n', 'color': 'black'},
{'id': 'r', 'color': 'red'},
{'id': 'b', 'color': 'dodgerblue'}];
svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,0 L4,2 L0,4 Z")
.attr('fill', function(d) { return d.color;});
function textSize(text, attr) {
var t = svg.append('text').text(text);
if (typeof(attr) != 'undefined') {
for (a in attr) {
t.attr(a, attr[a]);
}
}
var bbox = t.node().getBBox();
t.remove();
return bbox;
}
function exists(x) {
return typeof(x) != 'undefined';
}
var dotGraph;
var graph = {};
var nodes = [];
var edges = [];
var layout;
var scaleDotX;
var scaleDotY;
d3.text(path, function(data) {
dotGraph = graphlibDot.parse(data);
// Calculate width and height
var posMax = [0, 0];
for (var nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.value.pos = node.value.pos.split(',').map(function(d) {return parseInt(d);});
node.value.width = parseInt(node.value.width);
node.value.height = parseInt(node.value.height);
posMax[0] = Math.max(posMax[0], node.value.pos[0] + node.value.width);
posMax[1] = Math.max(posMax[1], node.value.pos[1] + node.value.height);
}
dotWidth = posMax[0];
dotHeight = posMax[1];
//svg.attr('viewBox', '0,0,' + dotWidth + ',' + dotHeight);
scaleDotX = d3.scale.linear().domain([0, dotWidth]).range([20, dotWidth * 1.2]);
scaleDotY = d3.scale.linear().domain([0, dotHeight]).range([20, dotHeight * 1.2]);
// Parse nodes
var i = 0;
for (nodeId in dotGraph._nodes) {
var node = dotGraph._nodes[nodeId];
node.index = i++;
// x, y is center of node (not corner)
node.x = scaleDotX(node.value.pos[0]);
node.y = scaleDotY(dotHeight - (node.value.pos[1] + node.value.height));
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
node.fixed = true;
nodes.push(node);
dotGraph._nodes[nodeId] = node;
}
// Parse edges
for (edgeId in dotGraph._edges) {
var edge = dotGraph._edges[edgeId];
edge.source = dotGraph._nodes[edge.u].index;
edge.target = dotGraph._nodes[edge.v].index;
var size = textSize(edge.value.label, {'class': 'edgeLabelText'});
edge.value.width = size.width + 2 * pad;
edge.value.height = size.height + 2 * pad;
if (!exists(edge.value.color)) {
edge.value.color = 'black';
}
switch (edge.value.color) {
case 'dodgerblue':
edge.value.type = 'b';
break;
case 'red':
edge.value.type = 'r';
break;
default:
edge.value.type = 'n';
}
edges.push(edge);
dotGraph._edges[edgeId] = edge;
}
// Setup graph
graph['nodes'] = nodes;
graph['edges'] = edges;
var isEdgeOver = false;
var isEdgeLabelOver = false;
// Add edges
edges = pane.append('g').attr('id', 'edges').selectAll('path').data(graph['edges']).enter().append('path')
.attr('class', 'edge')
.attr('stroke', function(d) {return d.value.color;})
.attr('marker-mid', function(d) { return 'url(#edgeArrow_' + d.value.type + ')';})
.on('mouseover', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 1.0);
edgeDiv.transition()
.duration(200)
.style('opacity', .9);
edgeDiv
.html(d.value.label)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
})
.on('mouseout', function(d) {
var edge = d3.select(this);
edge.transition()
.duration(200)
.style('opacity', 0.4);
edgeDiv.transition()
.duration(200)
.style('opacity', 0);
});
// Add nodes
nodes = pane.append('g').attr('id', 'nodes').selectAll('g').data(graph['nodes']).enter().append('g');
function fillColor(f) {
return typeof(f) == 'undefined' ? 'white' : f;
}
function setNodeSize(node) {
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
}
function updateNode(d, node) {
var shape;
if (d.value.shape == 'ellipse') {
node.selectAll('ellipse').remove();
shape = node.append('ellipse')
.attr('class', 'nodeEllipse')
.attr('cx', d.value.cx)
.attr('cy', d.value.cy)
.attr('rx', d.value.width * 0.6)
.attr('ry', d.value.height * 0.6);
} else {
node.selectAll('rect').remove();
shape = node.append('rect')
.attr('class', 'nodeRect')
.attr('width', d.value.width)
.attr('height', d.value.height);
}
shape.attr('fill', fillColor(d.value.fillcolor));
node.selectAll('text').remove();
var text = node.append('text')
.attr('class', 'nodeText')
.attr('x', pad)
.attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;})
.on('dblclick', releaseNode);
}
nodes.each(function(d) {var node = d3.select(this); updateNode(d, node);});
// TODO: activate again without interfering with click event
//nodes.on('dblclick', releaseNode);
nodes.on('click', function(d) {
var pos = this.getBBox();
var node = d3.select(this);
if (d3.event.defaultPrevented) return;
var form = node.append('foreignObject')
.attr('x', pos.x)
.attr('y', pos.y)
.attr('width', d.value.width)
.attr('height', 25);
var input = form.append('xhtml:form').append('input')
.attr('style', 'width: ' + d.value.width + 'px')
.attr('value', function() {
this.focus();
return d.value.label;
})
.on('blur', function() {
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
})
.on('keypress', function() {
if (!d3.event) {
d3.event = window.event;
}
var event = d3.event;
if (event.keyCode == 13) {
if (typeof(event.cancelBubble)) {
event.cancelBubble = true;
}
if (event.stopPropagation) {
event.stopPropagation();
}
event.preventDefault();
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check thi
}
});
});
nodes.on('mouseover', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source == node || d.target == node) {
edge.transition()
.duration(200)
.style('opacity', 1.0);
}
});
});
nodes.on('mouseout', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source.index == node.index || d.target.index == node.index) {
edge.transition()
.duration(200)
.style('opacity', 0.4);
}
});
});
// Zoom and translate event handler
function zoom(d) {
pane.attr('transform', 'translate(' + d3.event.translate + ') scale(' + d3.event.scale + ')');
}
// Force layout
layout = d3.layout.force()
.nodes(graph['nodes'])
.links(graph['edges'])
.size([dotWidth, dotHeight])
.charge(-3000)
.linkDistance(50)
.linkStrength(0.1)
.on('tick', updateGraph);
// Drag behavour
var drag = layout.drag()
.on('dragstart', function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
d.fixed = true;
});
nodes.call(drag);
// Zoom behaviour
var bZoom = d3.behavior.zoom()
.scaleExtent([0.2, 8])
//.on("dblclick.zoom", null)
.on('zoom', zoom);
svg.call(bZoom);
svg.on("dblclick.zoom", null);
// Start force layout
layout.start();
});
function length(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}
function pathPos(x1, y1, x2, y2, c) {
x = (1 - c) * x1 + c * x2;
y = (1 - c) * y1 + c * y2;
p = x + ',' + y;
return p;
}
// Update graph
function updateGraph() {
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; });
// Update edges
edges.attr('d', function(d) {
var dist = 100;
var l = length(d.source.x, d.source.y, d.target.x, d.target.y);
var n = Math.max(2, Math.floor(l / dist));
var marker = [];
for (var i = 1; i < n; ++i) {
marker.push(i / n);
}
var markerPos = marker.map(function(c) {return pathPos(d.source.x, d.source.y, d.target.x, d.target.y, c);});
var markerPos = ' L' + markerPos.join(' L');
return 'M' + d.source.x + ',' + d.source.y + markerPos + ' L' + d.target.x + ',' + d.target.y;
});
}
function releaseNode(d) {
d.fixed = false;
layout.start();
}
function releaseNodes() {
graph['nodes'].forEach (function (d) {
d.fixed = false;
});
layout.start();
}
function resetNodes() {
layout.stop();
var nodes = graph['nodes'];
nodes.forEach(function (node, i){
nodes[i].x = scaleDotX(node.value.pos[0]);
nodes[i].y = scaleDotY(dotHeight - (node.value.pos[1] + node.value.height));
nodes[i].px = nodes[i].x;
nodes[i].py = nodes[i].y;
nodes[i].fixed = true;
});
updateGraph();
layout.start();
}
</script>
</body>
</html>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# Authors: Christof Angermueller <cangermueller@gmail.com> # Authors: Christof Angermueller <cangermueller@gmail.com>
import os.path import os.path
from theano.printing import pydotprint from theano.printing import pydotprint
...@@ -46,8 +47,11 @@ def d3print(fct, outfile=None, return_html=False, print_message=True, ...@@ -46,8 +47,11 @@ def d3print(fct, outfile=None, return_html=False, print_message=True,
:param *args, **kwargs: Parameters passed to pydotprint :param *args, **kwargs: Parameters passed to pydotprint
""" """
# Generate dot graph definition by calling pydotprint # Generate dot graph by pydotprint and write to file
dot_graph = d3dot(fct, *args, **kwargs) dot_graph = d3dot(fct, *args, **kwargs)
dot_file = os.path.splitext(outfile)[0] + '.dot'
with open(dot_file, 'w') as f:
f.write(dot_graph)
# Read template HTML file and replace variables # Read template HTML file and replace variables
template_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), template_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
...@@ -56,7 +60,7 @@ def d3print(fct, outfile=None, return_html=False, print_message=True, ...@@ -56,7 +60,7 @@ def d3print(fct, outfile=None, return_html=False, print_message=True,
template = f.read() template = f.read()
f.close() f.close()
replace = { replace = {
'%% DOT_GRAPH %%': dot_graph, '%% DOT_FILE %%': dot_file,
} }
html = replace_patterns(template, replace) html = replace_patterns(template, replace)
......
...@@ -8,13 +8,13 @@ ...@@ -8,13 +8,13 @@
<body> <body>
<style> <style>
.svg { svg {
margin-left:auto; margin-left:auto;
margin-right:auto; margin-right:auto;
display:block; display:block;
position: fixed; position: fixed;
border: 0px solid black; border: 0px solid black;
top:0%; left:0%; right:0% bottom=0% top:5%; left:0%; right:0% bottom=10%
} }
.nodeRect { .nodeRect {
stroke: black; stroke: black;
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
color: black; color: black;
} }
.edge { .edge {
stroke: black;
stroke-width: 3px; stroke-width: 3px;
cursor: pointer; cursor: pointer;
opacity: 0.4;
} }
.edgeLabelRect { .edgeLabelRect {
stroke: black; stroke: black;
...@@ -43,37 +43,76 @@ ...@@ -43,37 +43,76 @@
text-anchor: start; text-anchor: start;
} }
.arrowHead { .arrowHead {
stroke: black; stroke: green;
stroke-width: 1px;
}
.arrowHead_n {
stroke: green;
}
.arrowHead_r {
stroke-width: 3px; stroke-width: 3px;
fill: black; fill: red;
stroke: red;
}
.arrowHead_b {
stroke: dodgerblue;
}
.tooltip {
position: absolute;
text-align: center;
vertical-align: middle;
min-width: 10px;
min-height: 10px;
padding: 5px;
background: lightsteelblue;
border: 1px solid black;
border-radius: 8px;
pointer-events: none;
} }
</style> </style>
<div>
<input name="resetNodes"
type="button"
value="Reset nodes"
onclick="resetNodes()"/>
<input name="releaseNodes"
type="button"
value="Release nodes"
onclick="releaseNodes()"/>
</div>
<script type="text/javascript"> <script type="text/javascript">
var width = 1200; var path='%% DOT_FILE %%';
var height = 900;
// Global attributes // Global attributes
var pad = 10; var pad = 10;
d3.select('body').select('svg').remove(); d3.select('body').select('svg').remove();
var svg = d3.select('body').append('svg') var svg = d3.select('body').append('svg')
.attr('class', 'svg')
.attr('width', '100%') .attr('width', '100%')
.attr('height', '100%'); .attr('height', '95%');
var pane = svg.append('g'); var pane = svg.append('g');
var edgeDiv = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0.0);
// Definition head of edges // Definition head of edges
svg.append("defs").append("marker") var markerData = [
.attr("id", 'edgeArrow') {'id': 'n', 'color': 'black'},
.attr("markerWidth", 6) {'id': 'r', 'color': 'red'},
.attr("markerHeight", 6) {'id': 'b', 'color': 'dodgerblue'}];
.attr("refX", 3) svg.append("defs").selectAll('marker').data(markerData).enter().append("marker")
.attr("refY", 3) .attr("id", function(d) { return 'edgeArrow_' + d.id;})
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refX", 2)
.attr("refY", 2)
.attr("orient", "auto") .attr("orient", "auto")
.append("path") .append("path")
.attr("d", "M0,0 L6,3 L0,6 Z") .attr("d", "M0,0 L4,2 L0,4 Z")
.attr('style', 'arrowHead'); .attr('fill', function(d) { return d.color;});
function textSize(text, attr) { function textSize(text, attr) {
var t = svg.append('text').text(text); var t = svg.append('text').text(text);
...@@ -86,15 +125,22 @@ ...@@ -86,15 +125,22 @@
t.remove(); t.remove();
return bbox; return bbox;
} }
function exists(x) {
return typeof(x) != 'undefined';
}
var dotGraph; var dotGraph;
var graph = {}; var graph = {};
var nodes = []; var nodes = [];
var edges = []; var edges = [];
// d3.text(path, function(dotGraphDef) { var layout;
var scaleDotX;
var dotGraphDef = '%% DOT_GRAPH %%'; var scaleDotY;
dotGraph = graphlibDot.parse(dotGraphDef);
d3.text(path, function(data) {
dotGraph = graphlibDot.parse(data);
// Calculate width and height // Calculate width and height
var posMax = [0, 0]; var posMax = [0, 0];
...@@ -108,10 +154,10 @@ ...@@ -108,10 +154,10 @@
} }
dotWidth = posMax[0]; dotWidth = posMax[0];
dotHeight = posMax[1]; dotHeight = posMax[1];
svg.attr('viewBox', '0,0,' + width + ',' + height); //svg.attr('viewBox', '0,0,' + dotWidth + ',' + dotHeight);
var scaleDotX = d3.scale.linear().domain([0, dotWidth + 100]).range([0, width]); scaleDotX = d3.scale.linear().domain([0, dotWidth]).range([20, dotWidth * 1.2]);
var scaleDotY = d3.scale.linear().domain([-25, dotHeight + 100]).range([0, height]); scaleDotY = d3.scale.linear().domain([0, dotHeight]).range([20, dotHeight * 1.2]);
// Parse nodes // Parse nodes
var i = 0; var i = 0;
...@@ -140,6 +186,19 @@ ...@@ -140,6 +186,19 @@
var size = textSize(edge.value.label, {'class': 'edgeLabelText'}); var size = textSize(edge.value.label, {'class': 'edgeLabelText'});
edge.value.width = size.width + 2 * pad; edge.value.width = size.width + 2 * pad;
edge.value.height = size.height + 2 * pad; edge.value.height = size.height + 2 * pad;
if (!exists(edge.value.color)) {
edge.value.color = 'black';
}
switch (edge.value.color) {
case 'dodgerblue':
edge.value.type = 'b';
break;
case 'red':
edge.value.type = 'r';
break;
default:
edge.value.type = 'n';
}
edges.push(edge); edges.push(edge);
dotGraph._edges[edgeId] = edge; dotGraph._edges[edgeId] = edge;
} }
...@@ -154,57 +213,51 @@ ...@@ -154,57 +213,51 @@
// Add edges // Add edges
edges = pane.append('g').attr('id', 'edges').selectAll('path').data(graph['edges']).enter().append('path') edges = pane.append('g').attr('id', 'edges').selectAll('path').data(graph['edges']).enter().append('path')
.attr('class', 'edge') .attr('class', 'edge')
.attr('marker-mid', 'url(#edgeArrow)') .attr('stroke', function(d) {return d.value.color;})
.attr('marker-mid', function(d) { return 'url(#edgeArrow_' + d.value.type + ')';})
.on('mouseover', function(d) { .on('mouseover', function(d) {
var edgeLabel = pane.select('#edgeLabels .' + d.id); var edge = d3.select(this);
edgeLabel.attr('style', 'display: inline'); edge.transition()
.duration(200)
.style('opacity', 1.0);
edgeDiv.transition()
.duration(200)
.style('opacity', .9);
edgeDiv
.html(d.value.label)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
}) })
.on('mouseout', function(d) { .on('mouseout', function(d) {
var edge = d3.select(this);
if (!isEdgeLabelOver) { edge.transition()
var edgeLabel = pane.select('#edgeLabels .' + d.id); .duration(200)
edgeLabel.attr('style', 'display: none'); .style('opacity', 0.4);
} edgeDiv.transition()
.duration(200)
.style('opacity', 0);
}); });
// Add edge labels
edgeLabels = pane.append('g').attr('id', 'edgeLabels').selectAll('g').data(graph['edges']).enter().append('g')
.attr('style', 'display: none')
.attr('class', function(d) {return d.id;})
.on('mouseover', function(d) {
isEdgeLabelOver = true;
var edgeLabel = d3.select(this);
edgeLabel.attr('style', 'display: inline');
})
.on('mouseout', function(d) {
var edgeLabel = d3.select(this);
edgeLabel.attr('style', 'display: none');
isEdgeLabelOver = false;
});
var edgeLabelsRect = edgeLabels.append('rect')
.attr('class', 'edgeLabelRect')
.attr('fill', 'white')
.attr('width', function(d) {return d.value.width;})
.attr('height', function(d) {return d.value.height;})
.attr('rx', 5)
.attr('ry', 5);
var edgeLabelsText = edgeLabels.append('text')
.attr('class', 'edgeLabelText')
.attr('x', function(d) {return pad;})
.attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;});
// Add nodes // Add nodes
nodes = pane.append('g').attr('id', 'nodes').selectAll('g').data(graph['nodes']).enter().append('g'); nodes = pane.append('g').attr('id', 'nodes').selectAll('g').data(graph['nodes']).enter().append('g');
function fillColor(f) { function fillColor(f) {
return typeof(f) == 'undefined' ? 'white' : f; return typeof(f) == 'undefined' ? 'white' : f;
} }
nodes.each(function(d) {
sel = d3.select(this); function setNodeSize(node) {
var size = textSize(node.value.label, {'class': 'nodeText'});
node.value.width = size.width + 2 * pad;
node.value.height = size.height + 2 * pad;
node.value.cx = node.value.width / 2;
node.value.cy = node.value.height / 2;
}
function updateNode(d, node) {
var shape; var shape;
if (d.value.shape == 'ellipse') { if (d.value.shape == 'ellipse') {
shape = sel.append('ellipse') node.selectAll('ellipse').remove();
shape = node.append('ellipse')
.attr('class', 'nodeEllipse') .attr('class', 'nodeEllipse')
.attr('cx', d.value.cx) .attr('cx', d.value.cx)
.attr('cy', d.value.cy) .attr('cy', d.value.cy)
...@@ -212,38 +265,92 @@ ...@@ -212,38 +265,92 @@
.attr('ry', d.value.height * 0.6); .attr('ry', d.value.height * 0.6);
} else { } else {
shape = sel.append('rect') node.selectAll('rect').remove();
shape = node.append('rect')
.attr('class', 'nodeRect') .attr('class', 'nodeRect')
.attr('width', d.value.width) .attr('width', d.value.width)
.attr('height', d.value.height); .attr('height', d.value.height);
} }
shape.attr('fill', fillColor(d.value.fillcolor)); shape.attr('fill', fillColor(d.value.fillcolor));
});
var nodesText = nodes.append('text') node.selectAll('text').remove();
.attr('class', 'nodeText') var text = node.append('text')
.attr('x', pad) .attr('class', 'nodeText')
.attr('dy', function(d) {return d.value.height - pad - 5;}) .attr('x', pad)
.text(function(d) {return d.value.label;}); .attr('dy', function(d) {return d.value.height - pad - 5;})
.text(function(d) {return d.value.label;})
.on('dblclick', releaseNode);
}
nodes.each(function(d) {var node = d3.select(this); updateNode(d, node);});
// Update graph // TODO: activate again without interfering with click event
function updateGraph() { //nodes.on('dblclick', releaseNode);
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; }); nodes.on('click', function(d) {
// Update edges var pos = this.getBBox();
edges.attr('d', function(d) { var node = d3.select(this);
return 'M' + d.source.x + ',' + d.source.y + ' L' + 0.5 * (d.source.x + d.target.x) + ',' + 0.5 * (d.source.y + d.target.y) + ' L' + d.target.x + ',' + d.target.y; if (d3.event.defaultPrevented) return;
var form = node.append('foreignObject')
.attr('x', pos.x)
.attr('y', pos.y)
.attr('width', d.value.width)
.attr('height', 25);
var input = form.append('xhtml:form').append('input')
.attr('style', 'width: ' + d.value.width + 'px')
.attr('value', function() {
this.focus();
return d.value.label;
})
.on('blur', function() {
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check this
})
.on('keypress', function() {
if (!d3.event) {
d3.event = window.event;
}
var event = d3.event;
if (event.keyCode == 13) {
if (typeof(event.cancelBubble)) {
event.cancelBubble = true;
}
if (event.stopPropagation) {
event.stopPropagation();
}
event.preventDefault();
d.value.label = input.node().value;
setNodeSize(d);
updateNode(d, node);
form.remove(); // TODO: check thi
}
});
});
nodes.on('mouseover', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source == node || d.target == node) {
edge.transition()
.duration(200)
.style('opacity', 1.0);
}
}); });
// Update edge labels });
edgeLabels.attr('transform', function(d) {
return 'translate(' + (0.5 * (d.source.x + d.target.x - d.value.width)) + ',' + (0.5 * (d.source.y + d.target.y - d.value.height)) + ')'; nodes.on('mouseout', function(node) {
edges.each(function (d, i) {
var edge = d3.select(this);
if (d.source.index == node.index || d.target.index == node.index) {
edge.transition()
.duration(200)
.style('opacity', 0.4);
}
}); });
} });
// Drag-start event handler
function dragStart(d) {
d3.event.sourceEvent.stopPropagation();
d.fixed = true;
}
// Zoom and translate event handler // Zoom and translate event handler
function zoom(d) { function zoom(d) {
...@@ -251,30 +358,90 @@ ...@@ -251,30 +358,90 @@
} }
// Force layout // Force layout
var layout = d3.layout.force() layout = d3.layout.force()
.nodes(graph['nodes']) .nodes(graph['nodes'])
.links(graph['edges']) .links(graph['edges'])
.size([width, height]) .size([dotWidth, dotHeight])
.gravity(0.00) .charge(-3000)
.charge(-1000)
.linkDistance(50) .linkDistance(50)
.linkStrength(0.1) .linkStrength(0.1)
.on('tick', updateGraph); .on('tick', updateGraph);
// Drag behavour // Drag behavour
var drag = layout.drag() var drag = layout.drag()
.on('dragstart', dragStart); .on('dragstart', function(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
d.fixed = true;
});
nodes.call(drag); nodes.call(drag);
// Zoom behaviour // Zoom behaviour
var bZoom = d3.behavior.zoom() var bZoom = d3.behavior.zoom()
.scaleExtent([0.2, 8]) .scaleExtent([0.2, 8])
//.on("dblclick.zoom", null)
.on('zoom', zoom); .on('zoom', zoom);
svg.call(bZoom); svg.call(bZoom);
svg.on("dblclick.zoom", null);
// Start force layout // Start force layout
layout.start(); layout.start();
// }); });
function length(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
}
function pathPos(x1, y1, x2, y2, c) {
x = (1 - c) * x1 + c * x2;
y = (1 - c) * y1 + c * y2;
p = x + ',' + y;
return p;
}
// Update graph
function updateGraph() {
// Update nodes
nodes.attr('transform', function(d) { return 'translate(' + (d.x - d.value.cx) + ' ' + (d.y - d.value.cy) + ')'; });
// Update edges
edges.attr('d', function(d) {
var dist = 100;
var l = length(d.source.x, d.source.y, d.target.x, d.target.y);
var n = Math.max(2, Math.floor(l / dist));
var marker = [];
for (var i = 1; i < n; ++i) {
marker.push(i / n);
}
var markerPos = marker.map(function(c) {return pathPos(d.source.x, d.source.y, d.target.x, d.target.y, c);});
var markerPos = ' L' + markerPos.join(' L');
return 'M' + d.source.x + ',' + d.source.y + markerPos + ' L' + d.target.x + ',' + d.target.y;
});
}
function releaseNode(d) {
d.fixed = false;
layout.start();
}
function releaseNodes() {
graph['nodes'].forEach (function (d) {
d.fixed = false;
});
layout.start();
}
function resetNodes() {
layout.stop();
var nodes = graph['nodes'];
nodes.forEach(function (node, i){
nodes[i].x = scaleDotX(node.value.pos[0]);
nodes[i].y = scaleDotY(dotHeight - (node.value.pos[1] + node.value.height));
nodes[i].px = nodes[i].x;
nodes[i].py = nodes[i].y;
nodes[i].fixed = true;
});
updateGraph();
layout.start();
}
</script> </script>
</body> </body>
</html> </html>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论