forked from ZJONSSON's block: Newton's cradle
Created
February 18, 2019 11:54
-
-
Save codingforpleasure/6cee291c26808f02cde76dd364f354a2 to your computer and use it in GitHub Desktop.
Newton's cradle
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: mit |
This is a very rudimentary implementation of 2D elastic collisions.
Each node supposed to collide must contain a "radius" and a "mass". Fixed nodes are assumed to have "infinite mass"
The equations should maintain constant momentum in the system (less any friction)
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
/* | |
Newtonian collision checking (draft) | |
based on the paper: http://www.vobarian.com/collisions/2dcollisions2.pdf | |
[email protected] | |
*/ | |
if (typeof d3.z != "object") d3.z = {}; | |
(function() { | |
// Initiates fully elastic collision | |
d3.z.collide = function(force) { | |
force.on("tick.collide", function(d) { | |
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; | |
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(e) { | |
var nodes = force.nodes(),n=nodes.length,i=0 | |
console.log(n) | |
while(++i<n) { | |
var node = nodes[i],radius = n.radius || 0; | |
if ( ((node.x-radius) < x1) || ((node.x+radius) > x2) ) {node.px = 2*node.x - node.px} | |
if ( ((node.y-radius) < y1) || ((node.y+radius) > y2) ) {node.py = 2*node.y - node.py} | |
} | |
}) | |
} | |
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 type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js"></script> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></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"> | |
var w=960,h=500,nodes=[], wires=[], links=[], | |
no_strands = 2, no_links = 2; | |
svg=d3.select("body") | |
.append("svg:svg") | |
.attr("height",h) | |
.attr("width",w) | |
d3.range(no_strands).forEach(function(strand) { | |
var n = d3.range(no_links).map(function(d,i) { return {x:100+strand*20,y:100+d*25,fixed:false}}) | |
nodes=nodes.concat(n) | |
wires.push(n) | |
d3.range(n.length-1).forEach(function(d,i) { links.push({target:n[i],source:n[i+1]})}) | |
n[0].fixed=true // fix the top circle | |
n[n.length-1].mass=10 // and give the last ball mass and radius | |
n[n.length-1].radius=10 | |
}) | |
balls = svg.selectAll(".ball") | |
.data(nodes.filter(function(d) { return d.radius > 0})) | |
balls.enter() | |
.append("circle") | |
.attr("class","ball") | |
.attr("r",function(d) { return d.radius}) | |
lines = svg.selectAll("path") | |
.data(wires) | |
lines.enter() | |
.append("path") | |
.attr("class","wire") | |
force = d3.layout.force() | |
.linkStrength(10) | |
.linkDistance(25) | |
.friction(1) | |
.nodes(nodes).links(links) | |
.charge(0) | |
.on("tick.redraw",redraw) | |
.size([200,9999]).gravity(0.001) | |
.start() | |
balls.call(force.drag) | |
d3.z.collide(force) | |
function redraw() { | |
balls.attr("cx",function(d) { return d.x}) | |
balls.attr("cy",function(d) { return d.y}) | |
lines.attr("d", d3.svg.line().interpolate("cardinal") | |
.x(function(d) { return d.x}) | |
.y(function(d) { return d.y})) | |
force.resume() | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment