/* * force-directed graph example using canvas * simulates displaying server data */ (function () { "use strict"; var fd = (function () { fd = { width : 960, height : 500, canvas : '', ctx : '', color : d3.scale.category20(), init : function (error, clusterData) { var graph, clusterTree; if (error) { console.log('oops! bad data'); return; } clusterTree = fd.toTree(clusterData); graph = fd.flatten(clusterTree); fd.canvas = d3.select("#canvas-box").append("canvas") .attr("width", fd.width) .attr("height", fd.height) .node(); fd.render(graph); }, nestHosts : function (instances) { // given an array of instances, returns an object with a children array of hosts // with props suitable for tree display var hosts = { name : 'hosts', children : [] }; var nest = d3.nest() .key(function(d) { return d.host; }) .key(function(d) { return d.port; }) .entries(instances); nest.forEach(function (mbr) { var host = { name : mbr.key, children : [] }; mbr.values.forEach(function (instance) { instance.values[0].name = instance.values[0].host.replace('.somecorp.com','') + ':' + instance.values[0].port; host.children.push(instance.values[0]); }); hosts.children.push(host); }); return hosts; }, nestPods : function (dbs) { // given an array of databases, returns an array of pods // with props suitable for tree display var pods = { name : 'pods', children : [] }; var nest = d3.nest() .key(function(d) { return d.pod; }) .entries(dbs); nest.forEach(function (mbr) { var pod = { name : mbr.key, children : [] }; mbr.values.forEach(function (db) { db.name = db.pod + ':' + db.label; pod.children.push(db); }); pods.children.push(pod); }); return pods; }, flatten : function (root) { var nodes = []; var links = []; var ndx = 0; var gdx = 0; var buildlink = function(source, target) { return { source: source.id, target: target.id, value: source.id }; }; var randRange = function (min, max) { return parseInt(Math.round(min + Math.random() * (max - min))); }; var buildNode = function(node) { var colors = groupColor(node.group) var x = randRange(1, fd.width) var y = randRange(1, fd.height) return { name: node.name, group: node.group, id: ndx, fill: colors.fill, stroke: colors.stroke, x: x, y: y}; }; var groupColor = function(node) { var fill = fd.color(node.group); var stroke = d3.rgb(fill).darker(0.4).toString(); return { fill: fill, stroke: stroke }; }; var recurse = function (node, parent) { var colors; node.id = ndx; ndx += 1; node.group = gdx; if (parent) { links.push(buildlink(node, parent)); } if (node.children) { nodes.push(buildNode(node)); gdx += 1; node.children.forEach(function (child) { recurse(child, node) }); } else { colors = groupColor(node); node.leaf = true; node.fill = colors.fill; node.stroke = colors.stroke; node.x = randRange(1, fd.width) node.y = randRange(1, fd.height) nodes.push(node); } } recurse(root); console.log('nodes: ' + nodes.length) return { nodes: nodes, links: links }; }, toTree : function (clusterData) { var clusters = { name: 'clusters', children : [] } clusterData.forEach(function(clusterDatum) { var cluster = { name : clusterDatum.name, children : [] }; var hosts = fd.nestHosts(clusterDatum.instances); var pods = fd.nestPods(clusterDatum.databases); cluster.children.push(hosts); cluster.children.push(pods); clusters.children.push(cluster); }); return clusters; }, render: function(graph) { var ctx = fd.canvas.getContext("2d"); var fit = Math.sqrt(graph.nodes.length / (fd.width * fd.height)); var charge = (-1 / fit); var gravity = (8 * fit); var force = d3.layout.force() .charge(charge) .gravity(gravity) .linkDistance(2) .size([fd.width, fd.height]); force.nodes(graph.nodes) .links(graph.links) .start(); force.on("tick", function() { ctx.clearRect(0,0,fd.canvas.width,fd.canvas.height); ctx.strokeStyle = "rgba(150,150,150,0.6)"; ctx.lineWidth = 1; ctx.beginPath(); graph.links.forEach(function(d) { ctx.moveTo(d.source.x,d.source.y); ctx.lineTo(d.target.x,d.target.y); }); ctx.stroke(); ctx.lineWidth = 1.5; graph.nodes.forEach(function(d) { ctx.fillStyle = d.fill; ctx.strokeStyle = d.stroke; ctx.beginPath(); ctx.arc(d.x,d.y,5,0,2*Math.PI); ctx.fill(); ctx.stroke(); }); }); } }; return fd; }()); // end fd iife // kick the whole thing off d3.json('clusters.json', fd.init); }());