Skip to content

Instantly share code, notes, and snippets.

@DavidDeSimone
Last active November 10, 2022 17:30
Show Gist options
  • Select an option

  • Save DavidDeSimone/1a32a97913360c8e181e to your computer and use it in GitHub Desktop.

Select an option

Save DavidDeSimone/1a32a97913360c8e181e to your computer and use it in GitHub Desktop.
D3 Large (Static) Graph Canvas Visualization with Force Directed Layout

D3 example of a large graph visualization rendered via canvas and maintained via a render queue. Credit to Kai Chang (http://bl.ocks.org/syntagmatic/raw/3341641/) for the render queue. Useful example for those dealing with large graphs that would like to use FDL to generate an inital layout, and then go static afterwords. Inspired by Kai Changes render queue and sxv's interactive canvas demo (http://bl.ocks.org/sxv/4485778).

Currently rendering 10,000 nodes and 100,000 edges, generated at random.

<!-- Authored by David DeSimone. For free use -->
<!DOCTYPE html>
<script src="http://bl.ocks.org/syntagmatic/raw/3341641/render-queue.js"></script>
<script src="http://d3js.org/d3.v2.js"></script>
<body>
<script>
var WIDTH = 960;
var HEIGHT = 500;
var NUM_NODES = 10000;
var NUM_EDGES = 100000;
var links = [];
var nodes = [];
var cv = d3.select("body")
.append("canvas")
.attr("width", WIDTH)
.attr("height", HEIGHT);
var canvas = cv
.node()
.getContext("2d");
var getColor = d3.scale.linear()
.domain([0, 0.5, 1])
.range(["#ef2212", "#e7c767", "#2799df"])
.interpolate(d3.interpolateHcl);
function generateNodes(num) {
var nodes = d3.range(num).map(function(d, i) {
return {
x: Math.random() * WIDTH,
y: Math.random() * HEIGHT,
r: Math.random() * 10 + 3,
color: getColor(Math.random()),
type: "node"
};
});
return nodes;
};
function generateLinks(nodes, num) {
var links = d3.range(num).map(function(d) {
return {
source: nodes[Math.floor(Math.random() * nodes.length)],
target: nodes[Math.floor(Math.random() * nodes.length)],
color: getColor(Math.random()),
type: "link"
};
});
console.log(links);
return links;
};
var clear_canvas = function() {
canvas.clearRect(0,0,WIDTH, HEIGHT);
};
function renderNode(d) {
canvas.fillStyle = d.color;
canvas.beginPath();
canvas.fillRect(d.x, d.y, d.r / 2, d.r / 2);
canvas.stroke();
canvas.fill();
};
function renderEdge(e) {
console.log(e);
canvas.strokeStyle = e.color;
canvas.beginPath();
canvas.moveTo(e.source.x, e.source.y);
canvas.lineTo(e.target.x, e.target.y);
canvas.stroke();
canvas.fill();
};
function renderData(o) {
if(o.type === "node") {
renderNode(o);
} else
if(o.type === "link") {
renderEdge(o);
} else {
console.log("Error!");
}
}
function FDL(nodesArr, linksArr) {
var force = d3.layout.force()
.charge(-40)
.linkDistance(5)
.gravity(0.06)
.size([WIDTH, HEIGHT]);
force.nodes(nodesArr)
.links(linksArr);
force.start();
for(i = 0; i < 10; i++) force.tick();
force.stop();
return nodesArr.concat(linksArr);
};
var render = renderQueue(renderData).clear(clear_canvas);
var nodes = generateNodes(NUM_NODES);
var links = generateLinks(nodes, NUM_EDGES);
var data = FDL(nodes, links);
render(data);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment