Roughly layer nodes in a force-bubble layout by their grouping or category, resulting in a striped-like appearance.
Last active
September 14, 2016 03:32
-
-
Save emo-eth/9e30181cd074a6d605f2aabd1cfd1087 to your computer and use it in GitHub Desktop.
Layered force-directed bubbles
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
license=Apache License, 2.0 |
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> | |
<meta charset="utf-8"> | |
<body> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script> | |
/* | |
* Vars, scales, and things | |
*/ | |
var width = 960, | |
height = 500, | |
maxRadius = 12, | |
numNodes = 200, | |
numGroups = 10, | |
rowSpacing = height / numGroups, // space between each row for initial | |
// placement | |
mid = numGroups / 2, // which group to place at the middle of the | |
// simulation | |
rowMin = width / mid; // minimum width for outer edges (there are mid # of | |
// stripes above and below y=height/2) | |
var color = d3.scaleOrdinal(d3.schemeCategory10); | |
/* | |
* Setup | |
*/ | |
var svg = d3.select('body').append('svg') | |
.attr('height', height) | |
.attr('width', width); | |
// make the nodes, and position them in layers roughly making a diamond shape | |
var nodes = d3.range(numNodes).map(function() { | |
var group = Math.floor(Math.random() * numGroups); | |
var posneg = Math.random() < 0.5 ? -1 : 1; // left or right of x-middle | |
var iOffset = Math.abs(group - mid); // how many rows out from the center | |
// this node is | |
var xWidth = width - (iOffset * rowMin); // width of the row this node | |
// belongs to (works out to be roughly diamond shaped) | |
var r = Math.sqrt((group + 1) / numGroups * -Math.log(Math.random())) * maxRadius, | |
// fancy math borrowed from mbostock | |
d = { | |
group: group, | |
radius: r, | |
x: (width / 2) + (posneg * ((Math.random() * xWidth) / 2)), // place | |
// somewhere in the range of xWidth around the middle | |
y: (height / 2) + ((group - mid) * rowSpacing) + (Math.random() * rowSpacing) | |
// place somewhere in range of rowSpacing in its group's row | |
}; | |
return d; | |
}); | |
// Set up the force simulation with x/y forces and collision detection. | |
// Setting the x/y forces too strong jumbles up the middle nodes, so set | |
// their strength weaker than the default. Setting alphaDecay lower than the | |
// default (<0.0288...) gives the simulation more time to smooth out into a | |
// circle with weaker x/y forces (otherwise the shape can be pretty lumpy) | |
var simulation = d3.forceSimulation() | |
.force("xcenter", d3.forceX(width / 2).strength(0.07)) | |
.force("ycenter", d3.forceY(height / 2).strength(0.07)) | |
.force('collide', d3.forceCollide().radius(function(d) { | |
return d.radius + 0.5; | |
}).strength(1).iterations(10)) | |
.alphaDecay([0.007]); | |
// place the nodes as circles | |
var circles = svg.selectAll('circles') | |
.data(nodes) | |
.enter() | |
.append('circle') | |
.attr('cx', function(d) { | |
return d.x; | |
}).attr('cy', function(d) { | |
return d.y; | |
}).attr('r', function(d) { | |
return d.radius; | |
}) | |
.style('fill', function(d) { | |
return color(d.group); | |
}); | |
/* | |
* Runtime | |
*/ | |
// assign the nodes to the simulation and set the tick function it calls | |
simulation.nodes(nodes) | |
.on('tick', tick); | |
/* | |
* Functions | |
*/ | |
function tick() { | |
/* | |
* For each tick, re-draw the circles at their new positions. | |
*/ | |
circles.attr('cx', function(d) { | |
return d.x; | |
}) | |
.attr('cy', function(d) { | |
return d.y; | |
}); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment