Skip to content

Instantly share code, notes, and snippets.

@larsvers
Last active November 28, 2016 21:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larsvers/36bca23bdaf669ca0fe0a8213a57147c to your computer and use it in GitHub Desktop.
Save larsvers/36bca23bdaf669ca0fe0a8213a57147c to your computer and use it in GitHub Desktop.
Force split and unite on canvas
license: mit
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>winter force</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://d3js.org/d3.v4.js"></script>
<link rel="stylesheet" type="text/css" href="force.css">
<style type="text/css">
/* === Meyer Reset & border box (in moderation) === */
html, body {
margin: 0;
padding: 0;
border: 0;
font-family: Arial;
font-size: 100%;
vertical-align: baseline;
height: 100%;
box-sizing: border-box;
}
*, *:before, *:after{
box-sizing: inherit;
}
a:link, a:visited, a:hover, a:active {
color: #000;
}
/* === Canvas === */
canvas {
border: 1px solid #ccc;
margin: 50px;
margin-bottom: 10px;
}
button#split {
margin-left: 50px;
}
</style>
</head>
<body>
<div id="container"></div>
<button id="split">Split</button>
<button id="unite">Unite</button>
<script>
// === Globals === //
var width = 500, height = 250;
var simulation;
// === Set up canvas === //
var canvas = d3.select('#container').append('canvas').attr('width', width).attr('height', height);
var context = canvas.node().getContext('2d');
// === Start the simulation === //
getSimulationData();
// === Get simulation data === //
function getSimulationData() {
var nodes = [];
d3.range(200).forEach(function(el, i) {
var obj = {};
obj.cluster = 0;
obj.radius = 5;
obj.colour = '#9CCFE5';
obj.x = width/2 + (Math.random() - 10); // slightly left-skewed position
obj.y = height/2 + (Math.random());
nodes.push(obj);
}); // get men nodes
d3.range(50).forEach(function(el, i) {
var obj = {};
obj.cluster = 1;
obj.radius = 5;
obj.colour = '#9FE789';
obj.x = width/2 + (Math.random() + 10); // slightly right-skewed position
obj.y = height/2 + (Math.random());
nodes.push(obj);
}); // get women nodes
initSimulation(nodes); // kick off simulation
} // getSimulationData()
// === Set up simulation params === //
function initSimulation(nodes) {
simulation = d3.forceSimulation(nodes)
.alpha(.02)
.force('charge', d3.forceManyBody().strength(-30))
.force('xPos', d3.forceX(width/2).strength(1))
.force('yPos', d3.forceY(height/2).strength(1));
simulation.on('tick', ticked);
function ticked() {
context.clearRect(0, 0, width, height);
context.save();
nodes.forEach(drawNode);
context.restore()
} // ticked()
function drawNode(d) {
context.beginPath();
context.moveTo(d.x + d.radius, d.y);
context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI);
context.fillStyle = d.colour;
context.fill();
} // drawNode()
} // Set up the simulation
// === Button handler === //
d3.select('button#split').on('mousedown', function(d) {
simulation.stop();
simulation
.force('charge', d3.forceManyBody().strength(-5))
.force('xPos', d3.forceX(function(d) { return d.cluster === 0 ? width * 0.3 : width * 0.7; }) )
.force('yPos', d3.forceY(height/2));
simulation.alpha(0.5);
simulation.restart();
}); // button listener/handler
d3.select('button#unite').on('mousedown', function(d) {
simulation.stop();
simulation
.force('charge', d3.forceManyBody().strength(-5))
.force('xPos', d3.forceX(function(d) { return d.cluster === 0 ? width * 0.5 : width * 0.4; }) )
.force('yPos', d3.forceY(height/2))
.force('collision', d3.forceCollide(5));
simulation.alpha(0.5);
simulation.restart();
}); // button listener/handler
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment