Last active
August 29, 2015 14:10
-
-
Save ayende/03c6786fae3996659675 to your computer and use it in GitHub Desktop.
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> | |
<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