Skip to content

Instantly share code, notes, and snippets.

@AlexanderGraf
Last active December 17, 2015 15:39
Show Gist options
  • Save AlexanderGraf/5633169 to your computer and use it in GitHub Desktop.
Save AlexanderGraf/5633169 to your computer and use it in GitHub Desktop.
Visualization of a Minimum Spanning Tree (MST)

Visualization of a Minimum Spanning Tree (MST) for an undirected and closed small connected graph. The weights (labeled as "values") are indicated by the edge thickness and opacity. A thicker and darker line corresponds to a larger weight. The MST is in red (labeled as the boolean "type"). The MST itself is calculated elsewhere.

<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<link rel="stylesheet" type="text/css" href="index3.css">
</head>
<body>
<div id="vis"></div>
<script type="text/javascript" src="index2.js"></script>
</body>
</html>
"use strict"
var links = [{target:4,source:5,value:0.35,type:1},
{target:4,source:7,value:0.37,type:0},
{target:5,source:7,value:0.28,type:1},
{target:0,source:7,value:0.16,type:1},
{target:1,source:5,value:0.32,type:0},
{target:0,source:4,value:0.38,type:0},
{target:2,source:3,value:0.17,type:1},
{target:1,source:7,value:0.19,type:1},
{target:0,source:2,value:0.26,type:1},
{target:1,source:2,value:0.36,type:0},
{target:1,source:3,value:0.29,type:0},
{target:2,source:7,value:0.34,type:0},
{target:6,source:2,value:0.40,type:1},
{target:3,source:6,value:0.52,type:0},
{target:6,source:0,value:0.58,type:0},
{target:6,source:4,value:0.93,type:0}];
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
link.value = +link.value;
link.type = +link.type;
});
var width = 960,
height = 500;
var factor = 4;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(function(d) { return d.value*factor; }) //sort of works
.linkStrength(function(d) { return d.value/factor; }) //sort of works
.charge(-1000)
.on("tick", tick)
.start();
// Set the range
var v = d3.scale.linear().range([0, 1]);
// Scale the range of the data
v.domain([0, d3.max(links, function(d) { return d.value; })]);
// asign a type per value to encode opacity
links.forEach(function(link) {
if (v(link.value) <= 25) {
link.opacity = "twofive";
} else if (v(link.value) <= 50 && v(link.value) > 25) {
link.opacity = "fivezero";
} else if (v(link.value) <= 75 && v(link.value) > 50) {
link.opacity = "sevenfive";
} else if (v(link.value) <= 100 && v(link.value) > 75) {
link.opacity = "onezerozero";
}
});
var svg = d3.select("#vis").append("svg")
.attr("width", width)
.attr("height", height);
// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.opacity; })
.style("stroke-width", function(d) { return d.value*10; });
var path2 = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.opacity; })
.style("stroke-width", 1);
// define the nodes
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("click", click)
.on("dblclick", dblclick)
.call(force.drag);
// add the nodes
node.append("circle")
.attr("r", 5);
// add the text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
// add the lines
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y;
return "M" +
d.source.x + "," +
d.source.y + "L" +
d.target.x + "," +
d.target.y;
});
path.style("opacity", function(d) { return d.value/1.3; });
path2.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y;
return "M" +
d.source.x + "," +
d.source.y + "L" +
d.target.x + "," +
d.target.y;
});
path2.style("opacity", function(d) {
if (d.type === 1) {
return 1;
} else {
return 0;
}
});
path2.style("stroke", "red")
.style("stroke-width", function(d) { return d.value*10; });
node
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
}
// action to take on mouse click
function click() {
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 22)
.style("fill", "steelblue")
.style("stroke", "lightsteelblue")
.style("stroke-width", ".5px")
.style("font", "20px sans-serif");
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 16)
.style("fill", "red");
}
// action to take on mouse double click
function dblclick() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 6)
.style("fill", "#ccc");
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 12)
.style("stroke", "none")
.style("fill", "black")
.style("stroke", "none")
.style("font", "10px sans-serif");
}
path.link {
fill: none;
stroke: green;
stroke-width: 1.5px;
}
path.link.twofive {
opacity: 0.25;
}
path.link.fivezero {
opacity: 0.50;
}
path.link.sevenfive {
opacity: 0.75;
}
path.link.onezerozero {
opacity: 1.0;
}
circle {
fill: #ccc;
stroke: #fff;
stroke-width: 1.5px;
}
text {
fill: #000;
font: 10px sans-serif;
pointer-events: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment