Skip to content

Instantly share code, notes, and snippets.

@cariaso
Created August 16, 2013 22:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cariaso/6254138 to your computer and use it in GitHub Desktop.
Save cariaso/6254138 to your computer and use it in GitHub Desktop.
Protovis force directed graph. Zooming works well
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>Protovis Graph</title>
<script type="text/javascript" src="http://cachedcommons.org/cache/jquery/1.4.2/javascripts/jquery-min.js"></script>
<script type="text/javascript" src="http://cachedcommons.org/cache/protovis/3.2.0/javascripts/protovis-min.js"></script>
<link rel="stylesheet" href="js.css">
<script type="text/javascript">
var buttonBarHeight = 30;
var redrawLag = 300;
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$(window).resize(function() {
delay(function(){
$(".resizable").resize();
}, redrawLag);
});
$(document).ready(function() {
$("#chart").resize(function() {
vis.width($("#chart").width());
vis.height($("#chart").height()-buttonBarHeight);
vis.render();
return false;
}
)
});
function dumpGraph(agraph) {
for (var oldidx in agraph.nodes) {
if (agraph.nodes[oldidx]) {
console.log(" name="+agraph.nodes[oldidx].nodeName+" is oldidx="+oldidx);
} else {
console.log(" hole at node#"+oldidx);
}
};
for (var oldidx in agraph.links) {
if (agraph.links[oldidx]) {
console.log(" link "+oldidx+" :: "+agraph.links[oldidx].source+" to "+agraph.links[oldidx].target);
} else {
console.log(" hole at link#"+oldidx);
}
};
}
function deleteSelected() {
// 2 pass
console.log("d was pressed. removing "+lastClicked.index + agraph.nodes[lastClicked.index].nodeName);
if (lastClicked) {
var numremoved = 0;
var removed = new Array();
for (v in agraph.links) {
if ((agraph.links[v].source == lastClicked.index) ||
(agraph.links[v].target == lastClicked.index)) {
//console.log("remove link #"+v+" :: "+agraph.nodes[agraph.links[v].source].nodeName + "--"+agraph.nodes[agraph.links[v].target].nodeName);
numremoved += 1;
agraph.links[v] = undefined;
removed.unshift(v);
} else if (agraph.links[v].source > lastClicked.index) {
//console.log("shifted link "+v+" source "+agraph.links[v].source+" -= "+numremoved);
agraph.links[v].source -= numremoved;
} else if (agraph.links[v].target > lastClicked.index) {
//console.log("shifted link "+v+" target "+agraph.links[v].target+" -= "+numremoved);
agraph.links[v].target -= numremoved;
}
}
for (v in removed) {
//console.log("removing link "+removed[v]);
agraph.links.splice(removed[v], 1);
}
//console.log("removing "+numremoved+" links and 1 node#"+lastClicked.index);
agraph.nodes.splice(lastClicked.index, 1);
//dumpGraph(agraph);
force.nodes(agraph.nodes);
force.links(agraph.links);
force.reset();
vis.render();
}
}
$(document).bind("keypress", "d", deleteSelected);
function resetGraph() {
for (v in agraph.nodes) {
agraph.nodes[v].x = NaN;
agraph.nodes[v].y = NaN;
agraph.nodes[v].px = NaN;
agraph.nodes[v].py = NaN;
}
force.reset().render()
}
function freezeGraph() {
for (v in agraph.nodes) {
d = agraph.nodes[v];
if (d.fix) {
d.fix = undefined;
} else {
d.fix = new pv.Vector(d.x,d.y);
}
}
force.reset().render()
}
</script> <style type="text/css">
body, html {
height: 100%;
margin: 0;
}
#chart { width: 100%; height: 100%; background: gray; border: 0px solid black;}
</style>
</head>
<body>
<div id="chart" class="resizable">
<script type="text/javascript+protovis">
var agraph = {
nodes:[
{nodeName:"gs145", group:1, size:120, color:"#FFFFD0"},
{nodeName:"rs12255372(T;T)", group:1, size:105, color:"#FF9090", gene:"TCF7L2"},
{nodeName:"Breast cancer", group:2, size:20, color:"blue"},
{nodeName:"Prostate cancer", group:2, size:20, color:"blue"},
{nodeName:"Type-2 diabetes", group:2, size:20, color:"blue"},
{nodeName:"rs6983267(G;G)", group:1, size:96, color:"#FF9090"},
{nodeName:"23andMe/SNPs", group:2, size:20, color:"blue"},
{nodeName:"Bladder cancer", group:2, size:20, color:"blue"},
{nodeName:"Cancer", group:2, size:20, color:"blue"},
{nodeName:"Colon cancer", group:2, size:20, color:"blue"},
{nodeName:"Colorectal cancer", group:2, size:20, color:"blue"},
{nodeName:"Coriell Personalized Medicine Collaborative", group:2, size:20, color:"blue"},
{nodeName:"Liver cancer", group:2, size:20, color:"blue"},
{nodeName:"Lung cancer", group:2, size:20, color:"blue"},
{nodeName:"rs2981582(T;T)", group:1, size:96, color:"#FF9090", gene:"FGFR2"},
{nodeName:"gs192", group:1, size:93, color:"#FFFFD0"},
{nodeName:"Homocystinuria", group:2, size:20, color:"blue"},
{nodeName:"gs227", group:1, size:90, color:"#FFFFD0"},
{nodeName:"Taste", group:2, size:20, color:"blue"},
{nodeName:"rs3803662(T;T)", group:1, size:90, color:"#FF9090", gene:"LOC643714"},
{nodeName:"rs16969968(A;A)", group:1, size:90, color:"#FF9090", gene:"CHRNA5"},
{nodeName:"Addiction", group:2, size:20, color:"blue"},
{nodeName:"Cholinesterase Inhibitors", group:2, size:20, color:"blue"},
{nodeName:"Chronic obstructive pulmonary disease", group:2, size:20, color:"blue"},
{nodeName:"ClinVar", group:2, size:20, color:"blue"},
{nodeName:"Nicotine dependence", group:2, size:20, color:"blue"},
{nodeName:"rs17602729(C;T)", group:1, size:90, color:"#FF9090", gene:"AMPD1"},
{nodeName:"Coronary artery disease", group:2, size:20, color:"blue"},
{nodeName:"rs2237717(T;T)", group:1, size:90, color:"#FF9090", gene:"MET"},
],
links:[
{source:1, target:2, value:1},
{source:1, target:3, value:1},
{source:1, target:4, value:1},
{source:5, target:6, value:1},
{source:5, target:7, value:1},
{source:5, target:8, value:1},
{source:5, target:9, value:1},
{source:5, target:10, value:1},
{source:5, target:11, value:1},
{source:5, target:12, value:1},
{source:5, target:13, value:1},
{source:5, target:3, value:1},
{source:14, target:2, value:1},
{source:14, target:11, value:1},
{source:15, target:16, value:1},
{source:17, target:18, value:1},
{source:19, target:2, value:1},
{source:20, target:21, value:1},
{source:20, target:22, value:1},
{source:20, target:23, value:1},
{source:20, target:24, value:1},
{source:20, target:13, value:1},
{source:20, target:25, value:1},
{source:26, target:24, value:1},
{source:26, target:27, value:1},
],
};
var w = $("#chart").width(), h = $("#chart").height()-buttonBarHeight;
var lastClicked;
var vis = new pv.Panel()
.width(w)
.height(h)
.fillStyle("white")
.event("mousedown", pv.Behavior.pan())
.event("mousewheel", pv.Behavior.zoom());
var force = vis.add(pv.Layout.Force)
.nodes(agraph.nodes)
.links(agraph.links);
force.link.add(pv.Line);
force.node.add(pv.Dot)
.size(function(d) d.size * Math.pow(this.scale, -1.5))
.fillStyle(function(d) d.color )
.strokeStyle(function() this.fillStyle().darker())
.lineWidth(1)
.title(function(d) {
if (d.desc) {
return d.desc;
} else if (d.effect) {
return d.effect;
} else {
var s = d.nodeName;
var g = d.gene;
if (g) s += " " +g;
return s;
}
})
.event("mousedown", pv.Behavior.drag())
.event("drag", force)
.event("click", function(d) {
if (lastClicked) lastClicked.size /= 5;
console.log("clicked on "+d.nodeName+" #"+d.index);
d.size *= 5;
lastClicked = d;
vis.render();
return false;
})
.event("dblclick", function(d) {
if (d.group == 2) {
setTag(d.nodeName);
} else {
if (d.fix) {
d.fix = None;
} else {
d.fix = new pv.Vector(d.x,d.y);
}
}
})
.anchor("left").add(pv.Label).text(function(d) { return d.nodeName} );
vis.render();
</script>
<button onclick="resetGraph();">Reset</button>
<button onclick="freezeGraph();">Freeze</button>
<span style="color:red"><b>d</b></span>elete
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment