Created
August 4, 2011 14:51
-
-
Save ralphbean/1125339 to your computer and use it in GitHub Desktop.
d3 viz for narcissus
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
<!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> |
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
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; | |
} |
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
$(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