Last active
July 6, 2016 16:15
-
-
Save darrenjaworski/d08c1cd33902dbc081d8bd8ff529103e to your computer and use it in GitHub Desktop.
Graph Algorithm Viz
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 lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Graphs Viz</title> | |
<style> | |
body { | |
max-width: 55em; | |
margin: 0 auto; | |
} | |
.links line { | |
stroke: #aaa; | |
stroke-width: 2; | |
} | |
.nodes circle { | |
pointer-events: all; | |
stroke: none; | |
fill: #aaa; | |
} | |
</style> | |
</head> | |
<body id="width"> | |
<svg></svg> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="script.js"></script> | |
</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
( function(){ | |
var nodeTotal = 50; | |
var edgeTotal = nodeTotal * 1.25; | |
var graph = { | |
nodes: [], | |
edges: [], | |
adjacent: [] | |
}; | |
graph.nodes = d3.range(nodeTotal).map(function(i) { | |
var node = { id : i }; | |
return node; | |
}); | |
graph.edges = d3.range(edgeTotal).map(function(i) { | |
var source = i < nodeTotal ? i : Math.floor(Math.random() * nodeTotal); | |
var target = Math.floor(Math.random() * nodeTotal); | |
var edge = { | |
source: source, | |
target: target | |
}; | |
if (graph.adjacent[source] === undefined) { | |
graph.adjacent[source] = []; | |
} | |
if (graph.adjacent[target] === undefined) { | |
graph.adjacent[target] = []; | |
} | |
graph.adjacent[source].push(target); | |
graph.adjacent[target].push(source); | |
return edge; | |
}); | |
var svg = d3.select("svg"), | |
width = document.getElementById('width').clientWidth, | |
height = width; | |
svg.attr('width', width) | |
.attr('height', height); | |
var simulation = d3.forceSimulation() | |
.force("link", d3.forceLink().id(function(d) { return d.id; })) | |
.force("charge", d3.forceManyBody()) | |
.force("center", d3.forceCenter(width / 2, height / 2)); | |
var link = svg.append("g") | |
.attr("class", "links") | |
.selectAll("line") | |
.data(graph.edges) | |
.enter().append("line"); | |
var node = svg.append("g") | |
.attr("class", "nodes") | |
.selectAll("circle") | |
.data(graph.nodes, function(d) { return d.id; }) | |
.enter().append("circle") | |
.attr("r", 5); | |
node.append("title") | |
.text(function(d) { return d.id; }); | |
simulation.nodes(graph.nodes) | |
.on("tick", ticked); | |
simulation.force("link") | |
.links(graph.edges); | |
function ticked() { | |
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; }); | |
node.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }); | |
} | |
var DepthFirstSearch = { | |
marked: [], | |
edgeTo: [], | |
source: 0, | |
count: 0, | |
dfs: function(graphObject, nodeIndex) { | |
var self = DepthFirstSearch; | |
dfs(graphObject, nodeIndex); | |
function dfs(graphObject, nodeIndex) { | |
self.marked[nodeIndex] = true; | |
self.count++; | |
graphObject.adjacent[nodeIndex].forEach(function(d){ | |
if (!self.marked[d]) { | |
self.edgeTo[d] = nodeIndex; | |
dfs(graphObject, d); | |
} | |
}); | |
return; | |
} | |
return; | |
}, | |
hasPathTo: function(endIndex) { | |
var self = DepthFirstSearch; | |
return self.marked[endIndex]; | |
}, | |
pathTo: function(graphObject, startIndex, endIndex) { | |
var self = DepthFirstSearch; | |
self.dfs(graphObject, startIndex); | |
if (!self.hasPathTo(endIndex)) { | |
return null; | |
} | |
var path = []; | |
for (var i = endIndex; i != startIndex; i = self.edgeTo[i]) { | |
path.push(i); | |
} | |
path.push(startIndex); | |
return path.reverse(); | |
} | |
}; | |
var BreadthFirstSearch = { | |
marked: [], | |
edgeTo: [], | |
count: 0, | |
bfs: function(graphObject, nodeIndex) { | |
var self = BreadthFirstSearch; | |
var queue = []; | |
self.marked[nodeIndex] = true; | |
queue.push(nodeIndex); | |
while (queue.length > 0) { | |
var v = queue.shift(); | |
graphObject.adjacent[v].forEach(markedAdjacent); | |
} | |
function markedAdjacent(d) { | |
if (!self.marked[d]) { | |
self.edgeTo[d] = v; | |
self.marked[d] = true; | |
queue.push(d); | |
} | |
} | |
}, | |
hasPathTo: function(endIndex){ | |
var self = BreadthFirstSearch; | |
return self.marked[endIndex]; | |
}, | |
pathTo: function(graphObject, startIndex, endIndex) { | |
var self = BreadthFirstSearch; | |
self.bfs(graphObject, startIndex); | |
if (!self.hasPathTo(endIndex)) { | |
return null; | |
} | |
var path = []; | |
for (var i = endIndex; i != startIndex; i = self.edgeTo[i]) { | |
path.push(i); | |
} | |
path.push(startIndex); | |
return path.reverse(); | |
} | |
}; | |
var start = Math.floor(Math.random() * nodeTotal); | |
var end = Math.floor(Math.random() * nodeTotal); | |
var dfpath = DepthFirstSearch.pathTo(graph, start, end); | |
var path = BreadthFirstSearch.pathTo(graph, start, end); | |
if (path !== null) { | |
var pathNodes = node.filter(function(d){ return path.indexOf(d.id) > -1; }) | |
.style('fill', '#000000'); | |
var pathEdges = link.filter(function(d){ | |
if (path.indexOf(d.source.id) < 0 || path.indexOf(d.target.id) < 0) { | |
return false; | |
} else { | |
var sourceIndex = path.indexOf(d.source.id); | |
var targetIndex = path.indexOf(d.target.id); | |
var check = path[sourceIndex - 1] == d.target.id || path[sourceIndex + 1] == d.target.id; | |
return check; | |
} | |
}).style('stroke', '#000000'); | |
} | |
} )(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment