|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title></title> |
|
<style> |
|
svg { |
|
background: #01243B; |
|
} |
|
|
|
p, h1 { |
|
font-family: Arial, Helvetica, sans-serif; |
|
} |
|
|
|
#container { |
|
width: 80%; |
|
} |
|
|
|
#left, #right { |
|
float: left; |
|
} |
|
|
|
#right { |
|
margin-left: 20px; |
|
} |
|
|
|
.node { |
|
fill: #336699; |
|
} |
|
|
|
.node.hover { |
|
fill: #5288DB; |
|
} |
|
|
|
.edge { |
|
stroke: #C5C5C5; |
|
stroke-width: 2; |
|
shape-rendering: geometricPrecision; |
|
} |
|
|
|
.edge.drag { |
|
opacity: 0.75; |
|
|
|
} |
|
|
|
.hidden { |
|
visibility: hidden; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="container"> |
|
<div id="left"> |
|
|
|
</div> |
|
<div id="right"> |
|
<p>Number Of Nodes: <span id="number-nodes"></span></p> |
|
<p>Number Of Edges: <span id="number-edges"></span></p> |
|
<p>Average Path Length: <span id="avg-path-length"></span></p> |
|
</div> |
|
</div> |
|
</body> |
|
<script src="./graph.js"></script> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.14/d3.js"></script> |
|
<script> |
|
// see https://stackoverflow.com/questions/14167863/how-can-i-bring-a-circle-to-the-front-with-d3 |
|
// call this method on a selection to move an SVG element to the front of the draw stack |
|
d3.selection.prototype.moveToFront = function() { |
|
return this.each(function(){ |
|
this.parentNode.appendChild(this); |
|
}); |
|
}; |
|
|
|
var width = 500; |
|
var height = 500; |
|
|
|
var ringRadius = 225; |
|
|
|
var graph = new Graph(60); |
|
var numberNodes = graph.numberNodes(); |
|
|
|
var nodes = new Array(numberNodes); |
|
var edges = []; |
|
|
|
var SELECTING = false; |
|
var nodeFrom = null; |
|
var nodeTo = null; |
|
|
|
// create node layout |
|
var delta = 2 * Math.PI / graph.numberNodes(); |
|
graph.nodes.forEach(function(d, i) { |
|
var coord = polarToCartesian(ringRadius, delta * i); |
|
d.x = width/2 + coord[0]; |
|
d.y = height/2 + coord[1]; |
|
}); |
|
|
|
// add edges between nodes |
|
for(var i = 0; i < numberNodes; i++) { |
|
graph.addEdge(i, (i + 1) % numberNodes); |
|
} |
|
|
|
var svg = d3.select("#left").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
svg.on('mousemove', function() { |
|
var mouse = d3.mouse(this); |
|
if(SELECTING) { |
|
dragLine |
|
.attr('x2', mouse[0]) |
|
.attr('y2', mouse[1]) |
|
.classed('hidden', false) |
|
.classed('drag', true); |
|
} |
|
}); |
|
|
|
// create edges |
|
svg.selectAll('.edge') |
|
.data(graph.edgeList()) |
|
.enter() |
|
.append('line') |
|
.attr('class', 'edge') |
|
.attr('x1', function(d) { |
|
var node = graph.nodes[d[0]]; |
|
return node.x; |
|
}) |
|
.attr('y1', function(d) { |
|
var node = graph.nodes[d[0]]; |
|
return node.y; |
|
}) |
|
.attr('x2', function(d) { |
|
var node = graph.nodes[d[1]]; |
|
return node.x; |
|
}) |
|
.attr('y2', function(d) { |
|
var node = graph.nodes[d[1]]; |
|
return node.y; |
|
}); |
|
|
|
// create drag line |
|
// this is hidden when a new edge is not being created |
|
var dragLine = svg.append('line') |
|
.classed('edge', true) |
|
.classed('hidden', true) |
|
.attr('x1', 0) |
|
.attr('y1', 0) |
|
.attr('x2', 0) |
|
.attr('y2', 0) |
|
.attr('stroke-dasharray', '5, 5'); |
|
|
|
// create nodes |
|
svg.selectAll('.node') |
|
.data(graph.nodes) |
|
.enter() |
|
.append('circle') |
|
.attr('class', 'node') |
|
.attr('cx', function(d) { return d.x }) |
|
.attr('cy', function(d) { return d.y }) |
|
.attr('r', 8) |
|
.on('mouseover', function() { |
|
d3.selectAll('.node') |
|
.classed('hover', false); |
|
|
|
d3.select(this) |
|
.classed('hover', true); |
|
}) |
|
.on('mouseleave', function() { |
|
d3.select(this) |
|
.classed('hover', false); |
|
}) |
|
.on('click', function(d) { |
|
if(!SELECTING) { |
|
nodeFrom = d3.select(this).moveToFront(); |
|
SELECTING = true; |
|
dragLine |
|
.attr('x1', d.x) |
|
.attr('y1', d.y) |
|
.attr('x2', d.x) |
|
.attr('y2', d.y) |
|
.classed('hidden', false); |
|
|
|
} |
|
else { |
|
nodeTo = d3.select(this); |
|
|
|
dragLine |
|
.classed('hidden', true); |
|
|
|
var fromId = nodeFrom.data()[0].id; |
|
var toId = nodeTo.data()[0].id; |
|
|
|
if(!graph.hasEdge(fromId, toId)) { |
|
svg.append('line') |
|
.attr('class', 'edge') |
|
.attr('x1', nodeFrom.data()[0].x) |
|
.attr('y1', nodeFrom.data()[0].y) |
|
.attr('x2', nodeTo.data()[0].x) |
|
.attr('y2', nodeTo.data()[0].y); |
|
|
|
graph.addEdge(fromId, toId); |
|
} |
|
|
|
nodeFrom.moveToFront(); |
|
nodeTo.moveToFront(); |
|
|
|
nodeFrom = false; |
|
nodeTo = false; |
|
SELECTING = false; |
|
|
|
updateDisplay(); |
|
} |
|
}); |
|
|
|
updateDisplay(); |
|
|
|
function updateDisplay() { |
|
document.getElementById('number-nodes').innerHTML = |
|
graph.numberNodes().toString(); |
|
document.getElementById('number-edges').innerHTML = |
|
graph.numberEdges().toString(); |
|
document.getElementById('avg-path-length').innerHTML = |
|
d3.round(graph.averagePathLength(), 2).toString(); |
|
} |
|
|
|
function polarToCartesian(r, theta) { |
|
var x = r * Math.cos(theta); |
|
var y = r * Math.sin(theta); |
|
return [x, y]; |
|
} |
|
</script> |
|
</html> |