Skip to content

Instantly share code, notes, and snippets.

@larskotthoff
Forked from djjupa/ReadMe.md
Last active August 29, 2015 13:56
Show Gist options
  • Save larskotthoff/8935723 to your computer and use it in GitHub Desktop.
Save larskotthoff/8935723 to your computer and use it in GitHub Desktop.

Collapsible force layout with directed paths and images/text in the nodes.

{
"name": "flare",
"logowidth": "36",
"logoheight": "36",
"logo": "http://www.amazon.com/favicon.ico",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
},
{
"name": "graph",
"children": [
{"name": "BetweennessCentrality", "size": 3534},
{"name": "LinkDistance", "size": 5731},
{"name": "MaxFlowMinCut", "size": 7840},
{"name": "ShortestPaths", "size": 5914},
{"name": "SpanningTree", "size": 3416}
]
},
{
"name": "optimization",
"children": [
{"name": "AspectRatioBanker", "size": 7074}
]
}
]
},
{
"name": "data",
"children": [
{
"name": "converters",
"children": [
{"name": "Converters", "size": 721},
{"name": "DelimitedTextConverter", "size": 4294},
{"name": "GraphMLConverter", "size": 9800},
{"name": "IDataConverter", "size": 1314},
{"name": "JSONConverter",
"logowidth": "36",
"logoheight": "36",
"logo": "http://wordpress.com/favicon.ico",
"size": 2220}
]
},
{"name": "DataField", "size": 1759},
{"name": "DataSchema", "size": 2165},
{"name": "DataSet", "size": 586},
{"name": "DataSource", "size": 3331},
{"name": "DataTable", "size": 772},
{"name": "DataUtil", "size": 3322}
]
},
{
"name": "display",
"children": [
{"name": "DirtySprite", "size": 8833},
{"name": "LineSprite", "size": 1732},
{"name": "RectSprite", "size": 3623},
{"name": "TextSprite", "size": 10066}
]
},
{
"name": "flex",
"children": [
{"name": "FlareVis", "size": 4116}
]
},
{
"name": "scale",
"children": [
{"name": "IScaleMap", "size": 2105},
{"name": "LinearScale", "size": 1316},
{"name": "LogScale", "size": 3151},
{"name": "OrdinalScale", "size": 3770},
{"name": "QuantileScale", "size": 2435},
{"name": "QuantitativeScale", "size": 4839},
{"name": "RootScale", "size": 1756},
{"name": "Scale", "size": 4268},
{"name": "ScaleType", "size": 1821},
{"name": "TimeScale", "size": 5833}
]
}
]
}
<meta charset="utf-8" />
<link href='http://fonts.googleapis.com/css?family=Life+Savers|Didact+Gothic' rel='stylesheet' type='text/css'>
<meta charset="utf-8" />
<style>
.node {
stroke: #009900;
stroke-width: 1.5px;
color: #009900;
}
.node text {
pointer-events: none;
font: 15px sans-serif;
stroke-width: 0px;
}
.link {
stroke: #999;
/* stroke-opacity: 2.6; */
}
path.link {
fill: none;
stroke-width: 2px;
}
marker#end {
fill: #999;
}
line {
stroke: #000;
stroke-width: 1.5px;
}
</style>
<link href='http://fonts.googleapis.com/css?family=Life+Savers|Didact+Gothic' rel='stylesheet' type='text/css'>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<body>
<script>
var w = 1200,
h = 800,
maxNodeSize = 50,
root;
var vis;
var force = d3.layout.force();
vis = d3.select("body").append("svg");
d3.json("flare.json", function(json) {
root = json;
root.fixed = true;
root.x = w / 2;
root.y = 120;
// Build the arrow
var defs = vis.insert("svg:defs").selectAll("marker")
.data(["end"]);
defs.enter().append("svg:marker")
.attr("id", "end") // As explained here: http://www.d3noob.org/2013/03/d3js-force-directed-graph-example-basic.html
.attr("viewBox", "0 -5 10 10")
.attr("refX", 10)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
update();
});
/**
*
*/
function update() {
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
// Restart the force layout.
force.nodes(nodes)
.links(links)
.gravity(0.05)
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.linkStrength(function(l, i) {return 1; })
.size([w, h])
.on("tick", tick)
.start();
var path = vis.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
path.enter().insert("svg:path")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.style("stroke", "#ccc");
path.enter().append("svg:image")
.attr("xlink:href", function(d) { return d.source.logo;})
.attr("x", function(d) { return d.source.x + (d.target.x - d.source.x)/2; })
.attr("y", function(d) { return d.source.y + (d.target.y - d.source.y)/2; })
.attr("height", function(d) { return d.source.logoheight || 16;})
.attr("width", function(d) { return d.source.logowidth || 16;});
// Exit any old paths.
path.exit().remove();
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id; });
// Enter any new nodes.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", click)
.call(force.drag);
// Append a circle
nodeEnter.append("svg:circle")
.attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
.style("fill", color);
/*
* Uncomment this in case you want to have text in your nodes
// Add text to the node (as defined by the json file)
nodeEnter.append("svg:text")
.attr("text-anchor", "middle")
.attr("dx", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
.attr("dy", ".35em")
.text(function(d) { return d.name; });
*/
// Exit any old nodes.
node.exit().remove();
// Re-select for update.
path = vis.selectAll("path.link");
node = vis.selectAll("g.node");
function tick() {
vis.selectAll("image")
.attr("x", function(d) { return d.source.x + (d.target.x - d.source.x)/2; })
.attr("y", function(d) { return d.source.y + (d.target.y - d.source.y)/2; });
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + ","
+ d.source.y
+ "A" + dr + ","
+ dr + " 0 0,1 "
+ d.target.x + ","
+ d.target.y;
});
node.attr("transform", nodeTransform);
}
}
/**
* Gives the coordinates of the border for keeping the nodes inside a frame
* http://bl.ocks.org/mbostock/1129492
*/
function nodeTransform(d) {
d.x = Math.max(maxNodeSize, Math.min(w - (d.logowidth/2 || 16), d.x));
d.y = Math.max(maxNodeSize, Math.min(h - (d.logoheight/2 || 16), d.y));
return "translate(" + d.x + "," + d.y + ")";
}
/**
* Color leaf nodes orange, and packages white or blue.
*/
function color(d) {
return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}
/**
* Toggle children on click.
*/
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
/**
* Returns a list of all nodes under the root.
*/
function flatten(root) {
var nodes = [];
var i = 0;
function recurse(node) {
if (node.children)
node.children.forEach(recurse);
if (!node.id)
node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment