Skip to content

Instantly share code, notes, and snippets.

@jdherg
Last active August 29, 2015 14:23
Show Gist options
  • Save jdherg/01f03a0ab5b6c2b78064 to your computer and use it in GitHub Desktop.
Save jdherg/01f03a0ab5b6c2b78064 to your computer and use it in GitHub Desktop.
Double Binary Bouncers
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.orbiter {
fill: #000;
}
.orbit {
fill: none;
stroke: #888;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<body>
<script>
var width = 960,
height = 500,
dot_r = 5,
orbit_r = height/8,
orbit_d = orbit_r * 2,
orbit_d2 = orbit_d * orbit_d,
center = {x: width/2,
y: height/2},
vx = 200,
vy = 0,
vt = Math.PI/800;
var dot_data = [
{ x_init: 0.25 * width - height/12,
y_init: 0.5 * height,
time_init: 0,
vx: vx,
vy: vy,
orbiting: false},
{ x_init: 0.25 * width + height/12,
y_init: 0.5 * height,
time_init: 0,
vx: -1 * vx,
vy: vy,
orbiting: false},
{ x_init: 0.75 * width - height/12,
y_init: 0.5 * height,
time_init: 0,
vx: vx,
vy: vy,
orbiting: false},
{ x_init: 0.75 * width + height/12,
y_init: 0.5 * height,
time_init: 0,
vx: -1 * vx,
vy: vy,
orbiting: false}
];
var orbit_data = [];
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var dots = svg.selectAll(".orbiter")
.data(dot_data, function(d,i) { return i; })
.enter().append("circle", true)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.attr("r", dot_r);
dots.classed("orbiter", true);
var orbits = svg.selectAll(".orbit")
.data(orbit_data, function(d,i) {
return d.orbit.x + " " + d.orbit.y + " " + d.time_init;
});
var animate = function(elapsed){
var cheat = false;
for (var i = 0; i < dot_data.length; i++) {
for (var j = i+1; j < dot_data.length; j++) {
if(!dot_data[i].orbiting && !dot_data[j].orbiting) {
var dx = dot_data[i].x - dot_data[j].x,
dy = dot_data[i].y - dot_data[j].y,
d2 = dx * dx + dy * dy;
if(d2 < orbit_d2) {
start_orbit(dot_data[i], dot_data[j], orbit_r, elapsed);
}
}
}
}
var orbits_to_delete = [];
for (i = 0; i < orbit_data.length; i++) {
var orbit = orbit_data[i];
if(elapsed - orbit_data[i].time_init >= (1 / vt * Math.PI)) {
orbits_to_delete.push(stop_orbit(orbit_data[i], elapsed));
cheat = true;
}
}
for (i = 0; i < orbits_to_delete.length; i++) {
orbit_data.splice(orbit_data.indexOf(orbits_to_delete[i]),1);
}
if(cheat) {
elapsed += 5;
}
dots.attr("cx", function(d){
if(d.orbiting) {
d.x = d.orbit.x +
Math.cos(d.t_init +
(elapsed - d.time_init)*vt
)*d.orbit.r;
} else {
if(d.x < 0) {
d.x *= -1;
d.time_init = elapsed;
d.x_init = d.x;
d.y_init = d.y;
d.vx *= -1;
} else if(d.x > width) {
d.x = 2 * width - d.x;
d.time_init = elapsed;
d.x_init = d.x;
d.y_init = d.y;
d.vx *= -1;
}
d.x = d.vx *
(elapsed - d.time_init)/1000 +
d.x_init;
}
return d.x;
})
.attr("cy", function(d){
if(d.orbiting) {
d.y = d.orbit.y +
Math.sin(d.t_init +
(elapsed - d.time_init)*vt
)*d.orbit.r;
} else {
if(d.y < 0) {
d.y *= -1;
d.time_init = elapsed;
d.y_init = d.y;
d.x_init = d.x;
d.vy *= -1;
} else if(d.y > height) {
d.y = 2 * height - d.y;
d.time_init = elapsed;
d.y_init = d.y;
d.x_init = d.x;
d.vy *= -1;
}
d.y = d.vy *
(elapsed - d.time_init)/1000 +
d.y_init;
}
return d.y;
});
orbits = orbits.data(
orbit_data, function(d) {return d.orbit.x + " " + d.orbit.y + " " + d.time_init;});
orbits.exit().remove();
orbits.enter().append("circle")
.classed("orbit",true)
.attr("cx", function(d){return d.orbit.x;})
.attr("cy", function(d){return d.orbit.y;})
.attr("r", function(d){return d.orbit.r;});
};
d3.timer(animate);
function start_orbit(a, b, r, elapsed){
var orbit = {
x: (a.x + b.x)/2,
y: (a.y + b.y)/2,
r: r
};
orbit_data.push(
{ orbit: orbit,
a: a,
b: b,
time_init: elapsed }
);
start_orbiter(a, orbit, elapsed);
start_orbiter(b, orbit, elapsed);
}
function start_orbiter(body, orbit, elapsed){
body.time_init = elapsed;
body.orbiting = true;
body.orbit = orbit;
body.t_init = find_theta(body, orbit);
}
function stop_orbit(orbit, elapsed) {
stop_orbiter(orbit.a, Math.PI, elapsed);
stop_orbiter(orbit.b, Math.PI, elapsed);
return orbit;
}
function stop_orbiter(body, delta_t, elapsed){
body.time_init = elapsed;
body.orbiting = false;
body.x_init = Math.round(body.orbit.x +
Math.cos(body.t_init + delta_t) * body.orbit.r);
body.y_init = Math.round(body.orbit.y +
Math.sin(body.t_init + delta_t) * body.orbit.r);
var vx = body.vx,
vy = body.vy;
body.orbit = null;
}
function find_theta(edge, center){
var dx = edge.x - center.x,
dy = edge.y - center.y,
d2 = dx * dx + dy * dy,
sin = dy / Math.sqrt(d2),
cos = dx / Math.sqrt(d2),
asin = Math.asin(sin),
acos = Math.acos(cos);
if(sin < 0) {
return -1 * acos;
} else {
if(cos < 0) {
return 2 * Math.PI - acos;
} else {
return acos;
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment