Skip to content

Instantly share code, notes, and snippets.

@enjalot
Last active March 12, 2016 19:10
Show Gist options
  • Save enjalot/6f9656f1ac033a1f4277 to your computer and use it in GitHub Desktop.
Save enjalot/6f9656f1ac033a1f4277 to your computer and use it in GitHub Desktop.
basic particle interaction
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body {
margin:0;position:fixed;top:0;right:0;bottom:0;left:0;
font-family: Helvetica;
}
svg { width:100%; height: 100% }
.controls {
position: absolute;
top: 30px;
left: 20px;
}
input.steps {
}
.num {
margin: 5px;
}
.current {
width: 50px;
}
</style>
</head>
<body>
<script>
// Feel free to change or delete any of the code you see!
var svg = d3.select("body").append("svg");
var dt = 0.05;
var numSteps = 60;
var a = {
index: 0,
radius: 40,
// initial position
x: 152, y: 206,
// initial velocity
vx: 61, vy: 0,
// initial acceleration
ax: 8, ay: 0
}
var b = {
index: 1,
radius: 40,
// initial position
x: 457, y: 251,
// initial velocity
vx: -69, vy: 0,
// initial acceleration
ax: 32, ay: -6
}
var nodes = [a,b];
var steps = [nodes]; // the collection of states
function step(nodes) {
var newNodes = [];
nodes.forEach(function(node) {
// copy the node
var newNode = {
index: node.index,
radius: node.radius
}
var ax = node.ax;
var ay = node.ay;
// go through all the neighbors and update vx and other properties
// update the velocity using latest accelerations
var vx = node.vx + ax * dt;
var vy = node.vy + ay * dt;
var x = node.x + vx * dt;
var y = node.y + vy * dt;
// copy the updated state into the new node
newNode.x = x;
newNode.y = y;
newNode.vx = vx;
newNode.vy = vy;
newNode.ax = ax;
newNode.ay = ay;
// we do collision detection on the positions directly
nodes.forEach(function(neighbor){
var xx = newNode.x - neighbor.x;
var yy = newNode.y - neighbor.y;
var dist = Math.sqrt(xx * xx + yy * yy);
var r = node.radius + neighbor.radius;
//console.log(dist, r)
if (dist < r) {
dist = (dist - r) / dist * dt; //don't quite understand this
newNode.x -= xx *= dist;
newNode.y -= yy *= dist;
neighbor.x += xx;
neighbor.y += yy;
}
})
newNodes.push(newNode)
})
return newNodes;
}
d3.range(numSteps).forEach(function(i) {
var lastState = steps[steps.length-1];
console.log("lastState", i, lastState)
var nextState = step(lastState);
steps.push(nextState)
})
console.log("steps", steps)
var gstep = svg.selectAll("g.step").data(steps)
gstep.enter().append("g").classed("step", true)
.style({
"stroke-opacity": function(d,i) { return 1 - i/numSteps }
})
.selectAll("circle.node").data(function(d) { return d })
.enter().append("circle").classed("node", true)
.attr({
r: function(d) { return d.radius },
cx: function(d) { return d.x },
cy: function(d) { return d.y },
fill: "none",
stroke: "#111",
//"stroke-dasharray": "4 4"
})
var current = svg.append("g").classed("current", true);
var controls = d3.select("body").append("div")
.classed("controls", true)
var curText = controls.append("div").text("step: ")
.append("span").classed("current", true).text(0)
controls
.append("input")
.classed("steps", true)
.attr({
type: "range",
value: 0,
min: 0,
max: numSteps
})
.on("input", slide)
.on("change", slide)
var numText = controls.append("span").classed("num", true).text(numSteps)
function slide() {
var i = +this.value || 0;
console.log(i);
var state = steps[i];
var circles = current.selectAll("circle.node").data(state)
circles.enter().append("circle").classed("node", true)
circles.attr({
r: function(d) { return d.radius },
cx: function(d) { return d.x },
cy: function(d) { return d.y },
fill: "#fff",
"fill-opacity": 0.5,
stroke: "#b41717",
})
curText.text(i)
}
slide();
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment