Built with blockbuilder.org
Last active
March 12, 2016 19:10
-
-
Save enjalot/6f9656f1ac033a1f4277 to your computer and use it in GitHub Desktop.
basic particle interaction
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> | |
<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