Last active
December 13, 2015 04:09
-
-
Save ZJONSSON/1706849 to your computer and use it in GitHub Desktop.
Newton's balls
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
/* global d3 */ | |
var svg=d3.select("body") | |
.append("svg") | |
.style("width","100%"); | |
var nodes = d3.range(400).map(function(d,i) { | |
var radius = Math.random()*10+5 + (i<0 ? 55 : 0), | |
x=Math.random()*300+radius, | |
y=Math.random()*300+radius; | |
return {radius:radius,mass:radius,x:x,y:y,px:x+Math.random()*2,py:y+Math.random()*2}; | |
}); | |
var ball = svg.selectAll(".ball") | |
.data(nodes) | |
.enter() | |
.append("circle") | |
.attr("class","ball") | |
.attr("r",function(d) { return d.radius; }); | |
function redraw() { | |
ball.attr("cx",function(d) { return d.x; }); | |
ball.attr("cy",function(d) { return d.y; }); | |
force.resume(); | |
} | |
var force = d3.layout.force() | |
.friction(1) | |
.nodes(nodes) | |
.charge(0).gravity(0) | |
.on("tick.redraw",redraw) | |
.start(); | |
ball.call(force.drag); | |
d3.z.collide(force); | |
function setBoundary() { | |
var w = svg.node().offsetWidth, | |
h = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight)-20; | |
svg.attr("height",h); | |
d3.z.deflect(force,0,0,w,h); | |
} | |
setBoundary(); | |
window.onresize = setBoundary; |
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
/* global d3 */ | |
/* | |
Newtonian collision checking (draft) | |
based on the paper: http://www.vobarian.com/collisions/2dcollisions2.pdf | |
ziggy.jonsson.nyc@gmail.com | |
*/ | |
if (typeof d3.z != "object") d3.z = {}; | |
(function() { | |
// Initiates fully elastic collision | |
d3.z.collide = function(force) { | |
force.on("tick.collide", function() { | |
var candidates = force.nodes().filter(function(d) { return d.radius && (d.mass > 0 || d.fixed);}), // Fixed nodes are always candidates | |
q = d3.geom.quadtree(candidates), | |
i = 0, | |
n = candidates.length, | |
node; | |
while (++i < n) { | |
node = candidates[i]; | |
if (node.mass >0) q.visit(d3.z.check_collision(node)); | |
} | |
}); | |
}; | |
d3.z.deflect = function(force,x1,y1,x2,y2) { | |
force.on("tick.deflect", function() { | |
var nodes = force.nodes(),n=nodes.length,i=0; | |
while(++i<n) { | |
var node = nodes[i],radius = node.radius || 0,newpos,speed; | |
if ( (node.x-radius < x1 && ((newpos = x1+radius) || true)) || (node.x+radius > x2 && ((newpos = x2-radius) || true))) { | |
speed = node.x - node.px; | |
node.x = newpos; | |
node.px = newpos + speed; | |
} | |
if ( (node.y-radius < y1 && ((newpos = y1+radius) || true)) || (node.y+radius > y2 && ((newpos = y2-radius) || true))) { | |
speed = node.y - node.py; | |
node.y = newpos; | |
node.py = newpos + speed; | |
} | |
} | |
}); | |
}; | |
var Vector = function(x,y) { | |
this.x = x; | |
this.y = y; | |
}; | |
Vector.prototype.add = function(d) { | |
return (typeof d == "object") ? new Vector(this.x+d.x,this.y+d.y) : new Vector(this.x+d,this.y+d); | |
}; | |
Vector.prototype.subtract = function(d) { | |
return (typeof d == "object") ? new Vector(this.x-d.x,this.y-d.y) : new Vector(this.x-d,this.y-d); | |
}; | |
Vector.prototype.mult = function(d) { | |
return (typeof d == "object") ? new Vector(this.x*d.x,this.y*d.y) : new Vector(this.x*d,this.y*d); | |
}; | |
Vector.prototype.dot = function(d) { | |
return (typeof d == "object") ? this.x*d.x+this.y*d.y : this.x*d+this.y*d; | |
}; | |
Vector.prototype.length = function() { | |
return Math.sqrt(this.dot(this)); | |
}; | |
Vector.prototype.norm = function() { | |
var length = this.length(); | |
return new Vector(this.x/length,this.y/length); | |
}; | |
Vector.prototype.tangent = function() { | |
return new Vector(this.y,-this.x); | |
}; | |
Vector.prototype.change = function(d,x,y) { | |
d[x ? x : "x"] = this.x; | |
d[y ? y : "y"] = this.y; | |
}; | |
d3.z.check_collision = function(node) { | |
var radius = node.radius, | |
nx1 = node.x - radius, | |
nx2 = node.x + radius, | |
ny1 = node.y - radius, | |
ny2 = node.y + radius; | |
return function(quad, x1, y1, x2, y2) { | |
if (quad.point && (quad.point !== node)) { | |
var b1 = node, | |
b2 = quad.point, | |
r = b1.radius+b2.radius, | |
_p1 = new Vector(b1.x,b1.y), | |
_p2 = new Vector(b2.x,b2.y), | |
_n = _p2.subtract(_p1), | |
l = _n.length(); | |
if (l < r) { | |
var m1 = b1.fixed ? 1e99 : b1.mass, | |
m2 = b2.fixed ? 1e99 : b2.mass, | |
totalmass = m1+m2, | |
mT = _n.norm().mult(r-l), | |
_v1 = new Vector(b1.x-b1.px,b1.y-b1.py), | |
_v2 = new Vector(b2.x-b2.px,b2.y-b2.py), | |
_un = _n.norm(), | |
_ut = new Vector(-_un.y,_un.x), | |
v1n = _un.dot(_v1), | |
v1t = _ut.dot(_v1), | |
v2n = _un.dot(_v2), | |
v2t = _ut.dot(_v2), | |
V1n = (v1n * (m1-m2) + 2*m2 * v2n ) / (totalmass), | |
V2n = (v2n * (m2-m1) + 2*m1 * v1n ) / (totalmass), | |
_V1 = _un.mult(V1n).add(_ut.mult(v1t)), | |
_V2 = _un.mult(V2n).add(_ut.mult(v2t)); | |
_p1 = _p1.subtract(mT.mult(m2/totalmass)); | |
_p2 = _p2.add(mT.mult(m1/totalmass)); | |
b1.x = _p1.x; | |
b1.y = _p1.y; | |
b2.x = _p2.x; | |
b2.y = _p2.y; | |
b1.px = b1.x - _V1.x; | |
b1.py = b1.y - _V1.y; | |
b2.px = b2.x - _V2.x; | |
b2.py = b2.y - _V2.y; | |
} | |
} | |
return x1 > nx2 || | |
x2 < nx1 || | |
y1 > ny2 || | |
y2 < ny1; | |
}; | |
}; | |
})(); |
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> | |
<html> | |
<head> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script type="text/javascript" src="d3.z.collide.js"></script> | |
<style> | |
.ball { fill:steelblue;stroke:black} | |
.wire {stroke:black;stroke-widh:1;fill:none} | |
</style> | |
</head> | |
<body> | |
<script type="text/javascript" src="app.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment