Skip to content

Instantly share code, notes, and snippets.

@ayende
Last active August 29, 2015 14:10
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 ayende/03c6786fae3996659675 to your computer and use it in GitHub Desktop.
Save ayende/03c6786fae3996659675 to your computer and use it in GitHub Desktop.
<!doctype html>
<meta charset="utf-8">
<title>HyParView visualization</title>
<link rel="stylesheet" href="http://cpettitt.github.io/project/dagre-d3/latest/demo/demo.css">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://cpettitt.github.io/project/dagre-d3/latest/dagre-d3.js"></script>
<style id="css">
body {
font: 300 14px 'Helvetica Neue', Helvetica;
color: pink;
}
.node rect {
stroke: #333;
fill: #fff;
}
.edgePath path {
stroke: #333;
fill: #333;
color: aqua;
stroke-width: 1.5px;
}
</style>
<h1>HyParView visualization</h1>
<svg width=960 height=960><g/></svg>
<section>
<script id="js">
var numberOfNodes = 24;
// Create a new directed graph
var g = new dagreD3.graphlib.Graph();
// Set an object for the graph label
g.setGraph({});
// Default to assigning a new object as a label for each new edge.
g.setDefaultEdgeLabel(function() { return {}; });
g.setDefaultNodeLabel(function(n) { return n.name + ' hello'; });
function seedableRandom() {
this.m_w = 123456789;
this.m_z = 987654321;
this.mask = 0xffffffff;
this.seed = function (i) {
m_w = i;
}
this.next = function()
{
this.m_z = (36969 * (this.m_z & 65535) + (this.m_z >> 16)) & this.mask;
this.m_w = (18000 * (this.m_w & 65535) + (this.m_w >> 16)) & this.mask;
var result = ((this.m_z << 16) + this.m_w) & this.mask;
result /= 4294967296;
return result + 0.5;
}
this.nextVal = function(max) {
return Math.floor((this.next() * max));
}
}
var rand = new seedableRandom();
function Node(name) {
this.name = name;
this.activeView = [];
this.passiveView = [];
this.activeRandomWalkLength = 3;
this.passiveRandomWalkLength = 2;
this.maxActiveView = 3;
this.maxPassiveView = 18;
var self = this;
this.heartbeat = function() {
if(self.activeView.length == 0 &&
self.passiveView.length ==0)
return; // shouldn't happen
var priority = 'low';
if(self.activeView.length == self.maxActiveView)
priority = 'passive';
else if (self.activeView.length == 0)
priority = 'high'
var node;
if(self.passiveView.length == 0)
{
// let us get us some passives
var randIndex = rand.nextVal(self.activeView.length);
node = self.activeView[randIndex];
}
else{
var randIndex = rand.nextVal(self.passiveView.length);
node = self.passiveView[randIndex];
}
node.neighbor(self, priority);
};
this.join = function(node) {
this.addNodeActiveView(node);
var ARWL = this.activeRandomWalkLength;
this.activeView.forEach(function(n) {
if(n === node)
{
//nothing to do
}
else
{
n.forwardJoin(node, ARWL, self);
}
})
}
this.forwardJoin = function(node, ttl, sender) {
if(node === this || this.activeView.indexOf(node) != -1)
return;
if(ttl == 0 || this.activeView.length <= 1){
this.addNodeActiveView(node)
}
else{
if(ttl == this.passiveRandomWalkLength){
this.addNodePassiveView(node);
}
var randIndex = rand.nextVal(this.activeView.length);
this.activeView[randIndex].forwardJoin(node, ttl - 1, this);
}
};
this.addNodeActiveView = function(node) {
if(this.activeView.length == this.maxActiveView){
this.dropRandomElementFromActiveView();
}
if(this.activeView.indexOf(node) != -1)
return;
var passiveIndex = this.passiveView.indexOf(node) ;
if(passiveIndex != -1)
this.passiveView.splice(passiveIndex,1);
this.activeView.push(node);
if(node.activeView.indexOf(this) != -1)
return;
passiveIndex = node.passiveView.indexOf(self) ;
if(passiveIndex != -1)
node.passiveView.splice(passiveIndex,1);
node.activeView.push(self);
}
this.dropRandomElementFromActiveView = function() {
var randIndex = rand.nextVal(this.activeView.length);
this.activeView[randIndex].disconnect(this);
this.passiveView.push(this.activeView[randIndex]);
this.activeView.splice(randIndex,1);
}
this.neighbor = function(node, priority){
if(this.activeView.length < this.maxActiveView && priority != 'passive' ||
priority == 'high' )
{
this.addNodeActiveView(node);
}
this.activeView.forEach(function(n){ node.addNodePassiveView(n) });
this.passiveView.forEach(function(n){ node.addNodePassiveView(n) });
}
this.disconnect = function(node) {
var idx = this.activeView.indexOf(node);
if(idx == -1)
return;
this.activeView.splice(idx,1);
this.addNodePassiveView(node);
}
this.addNodePassiveView = function(node) {
if (node == this ||
this.activeView.indexOf(node) != -1 ||
this.passiveView.indexOf(node) != -1)
return;
if(this.passiveView.length == this.maxPassiveView){
var randIndex = rand.nextVal(this.activeView.length);
this.passiveView.splice(randIndex,1);
}
this.passiveView.push(node);
}
}
var initialContact = new Node('A');
var nodes = [initialContact];
g.setNode(initialContact.name, initialContact);
setInterval(function(){
nodes.forEach(function(n) { n.heartbeat(); });
if(g.nodeCount() < numberOfNodes)
{
// add node to graph
var node = new Node(String.fromCharCode('A'.charCodeAt(0) + g.nodeCount()));
var randIndex = rand.nextVal(nodes.length);
nodes[randIndex].join(node);
nodes.push(node);
g.setNode(node.name, node);
g.edges().forEach(function(edge) {
g.removeEdge(edge);
});
}
// redraw
var edges = []
g.nodes().forEach(function(name) {
var n = g.node(name);
var names = [];
n.passiveView.forEach(function(a){ names.push(a.name); });
names.sort();
n.label = n.name +' [' + names.join() + ']'
n.activeView.forEach(function(o){
if(edges.indexOf(n.name +"->" + o.name) == -1)
{
edges.push(n.name +"->" + o.name);
edges.push(o.name +"->" + n.name);
g.setEdge(n.name, o.name, { arrowhead: 'none'});
}
});
});
render(inner, g);
}, 1000);
var svg = d3.select("svg"),
inner = svg.select("g");
// Set up zoom support
var zoom = d3.behavior.zoom().on("zoom", function() {
inner.attr("transform", "translate(" + d3.event.translate + ")" +
"scale(" + d3.event.scale + ")");
});
svg.call(zoom);
// Create the renderer
var render = new dagreD3.render();
render.arrows()['none'] = function() {};
// Run the renderer. This is what draws the final graph.
render(inner, g);
// Center the graph
var initialScale = 0.75;
zoom
.translate([(svg.attr("width") - g.graph().width * initialScale) / 2, 20])
.scale(initialScale)
.event(svg);
//svg.attr('height', g.graph().height * initialScale + 40);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment