Skip to content

Instantly share code, notes, and snippets.

@ralphbean
Created August 4, 2011 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ralphbean/1125339 to your computer and use it in GitHub Desktop.
Save ralphbean/1125339 to your computer and use it in GitHub Desktop.
d3 viz for narcissus
<!DOCTYPE html>
<html>
<head>
<title>Proof of concept for d3 viz + narcissus</title>
<link rel="stylesheet" type="text/css" href="spider.css"></style>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="spider.js"></script>
</head>
<body>
<div id="wrapper"><div id="container"></div></div>
</body>
</html>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
rect {
fill: white;
}
.transient {
fill: #2BAC82;
}
.country {
fill: #2F6AAB;
}
.distro {
fill: #C28931;
}
.link {
stroke: gray;
}
#wrapper {
width: 100%;
}
#container {
width: 960px;
margin: auto;
}
$(document).ready(function() {
var counter = 0;
var lifetime = 3000;
var base_radius = 1;
var bonus = 1;
var w = 960
var h = 700;
// Setup a 'center width' and 'center height'
var cw = w/2;
var ch = h/2;
// Just some constants for setting up dummy nodes.
var distros = [
"Fedora", "Ubuntu", "Slackjaw Linux", "Mooseknuckle"
];
var countries = [
"United States", "China", "Brazil", "The Congo",
"France", "Germany", "United Kingdom", "Greece", "Spain",
"Portugal", "Ireland", "Italy", "Mexico",
];
var fill = d3.scale.category20();
var nodes = [];
var links = [];
var vis = d3.select("#container").append("svg:svg")
.attr("width", w)
.attr("height", h);
vis.append("svg:rect")
.attr("width", w)
.attr("height", h);
var force = d3.layout.force()
.charge(-70)
.linkStrength(0.001)
.linkDistance(1000)
.nodes(nodes)
.links(links)
.size([w, h]);
force.on("tick", function() {
vis.selectAll("line.link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
vis.selectAll("circle.node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
function remove_links_for_node(node) {
for ( var i=0; i < links.length; i++ ) {
if ( links[i].source == node || links[i].target == node ) {
var link = links[i];
links.splice(i, 1);
link.source.r -= bonus;
link.target.r -= bonus;
// Assume that if the source or target, either,
// have a base_radius now, then they are
// isolated vertices with no links. Remove
// them!
if ( link.source.r == base_radius ) {
remove_node(link.source);
}
if ( link.target.r == base_radius ) {
remove_node(link.target);
}
return true;
}
}
return false;
}
function remove_node(node) {
for ( var i=0; i < nodes.length; i++ ) {
if ( nodes[i] == node ) {
nodes.splice(i, 1);
return true;
}
}
for ( var i=0; i < nodes.length; i++ ) {
if ( nodes[i] == node ) {
nodes.splice(i, 1);
return true;
}
}
while (remove_links_for_node(node)) {}
restart();
return false;
}
function find_node_by_id(id) {
for ( var i = 0; i < nodes.length; i++ ) {
if ( nodes[i].id == id ) {
return nodes[i];
}
}
return null;
}
function make_link(a, b) {
if ( a == b ) { return; }
link = {source: a, target: b};
links.push(link);
a.r += bonus;
b.r += bonus;
}
function make_transient(country, distro) {
// Find the country and distro nodes. If DNE, then create them.
var c_node = find_node_by_id(country);
var d_node = find_node_by_id(distro);
if ( c_node == null ) {
c_node = make_node(0, 0, "country", country);
}
if ( d_node == null ) {
d_node = make_node(0, 0, "distro", distro);
}
// Make this new hit node.
t_node = make_node(cw, ch, "transient", null);
// Make links between this node and its parents.
make_link(t_node, c_node);
make_link(t_node, d_node);
}
function make_node(x, y, cls, id) {
// Add the node
if ( id == null ) { id = counter; }
var node = {
x: x,
y: y,
r: base_radius,
id: id,
cls: cls,
ttl: lifetime,
};
nodes.push(node);
counter++;
// add links randomly
if (cls != "country" && cls != "distro" ) {
var targets = ["country", "distro"];
for ( var j = 0; j < targets.length; j++ ) {
var indices = [];
for ( var i = 0; i < nodes.length; i++ ) {
if ( nodes[i].cls == targets[j] ) {
indices.push(i);
}
}
i = Math.floor(Math.random()*indices.length);
var target = nodes[indices[i]];
make_link(node, target);
}
}
// schedule the removal of the node we just added
if ( cls == "transient" ) {
setTimeout(
function(){while(remove_node(node)){}}, node.ttl
);
}
return node;
}
function restart() {
var p;
// Handle the links
p = vis.selectAll("line.link")
.data(links,
function(d) { return d.source.id + "_" + d.target.id }
);
p.enter().insert("svg:line", "circle.node")
.attr("class", "link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
p.exit().remove();
// Handle the nodes
p = vis.selectAll("circle.node")
.data(nodes, function(d) { return d.id });
p.attr("r", function(d) { return d.r; })
p.enter().insert("svg:circle", "circle.cursor")
.attr("class", function(d) { return "node " + d.cls; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.on("mouseover", function(d) {d.r += 5*bonus;})
.on("mouseout", function(d) {d.r -= 5*bonus;})
.call(force.drag);
p.append("svg:title")
.text(function(d) { return d.id });
p.exit().remove();
// This is expensive, if we can avoid it doing it so much.
force.start();
}
// Trigger random creation of "server hits". To be replace by orbited.
setInterval(
function() {
var i = Math.floor(Math.random()*countries.length);
var j = Math.floor(Math.random()*distros.length);
make_transient(countries[i], distros[j]);
restart();
}, 100);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment