One click to place new planet, another click to define speed and direction. Try to create as many orbiting planets as you can.
function S(p,k){function t(f,e){f&&e&&(f.x+=e.x,f.y+=e.y)}function x(f,e){var g=f.x-e.x,b=f.y-e.y;n=Math.sqrt(g*g+b*b)}function u(f,e,g,b,d,h){return{x:f,y:e,r:g,m:b,s:d||g/5,c:h||"#08b"}}function y(f){q=f.pageX;r=f.pageY}var v=p.width,w=p.height,l=[u(v/2,w/2,20,0,200,"#fc3"),u(v/2,w/2+200,8,{x:1,y:0})],m,q,r,n;p.onclick=function(f){y(f);m?(m.m={x:(q-m.x)/99,y:(r-m.y)/99},l.push(m),m=0):m=u(q,r,5*Math.random()+5)};p.onmousemove=y;setInterval(function(){var f=l.length,e,g,b,d,h;for(e=f;e--;)for(b=
l[e],g=f;g--;)d=l[g],b.r&&d.r&&e-g&&(x(d,b),h=b.s*d.s/(n*n),t(b.m,{x:(d.x-b.x)/n*h,y:(d.y-b.y)/n*h}));for(e=f;e--;)t(l[e],l[e].m);for(e=f;e--;)for(b=l[e],g=f;g-- >e+1;)d=l[g],x(d,b),b.r&&d.r&&n<b.r+d.r&&(h=b.s<d.s?b:d,d=h==b?d:b,d.r+=h.r/2,d.s+=h.s/2,t(d.m,h.m),d.m&&(d.m.x/=2,d.m.y/=2),h.r=0);k.fillStyle="#222";k.fillRect(0,0,v,w);for(e=f+1;e--;)(b=l[e]||m)&&b.r&&(k.beginPath(),k.fillStyle=b.c,k.arc(b.x,b.y,b.r,0,7),k.fill(),b==m&&(k.strokeStyle=b.c,k.moveTo(b.x,b.y),k.lineTo(q,r),k.stroke()))},10)}
S(a,c);
ZnVuY3Rpb24gUyhwLGspe2Z1bmN0aW9uIHQoZixlKXtmJiZlJiYoZi54Kz1lLngsZi55Kz1lLnkpfWZ1bmN0aW9uIHgoZixlKXt2YXIgZz1mLngtZS54LGI9Zi55LWUueTtuPU1hdGguc3FydChnKmcrYipiKX1mdW5jdGlvbiB1KGYsZSxnLGIsZCxoKXtyZXR1cm57eDpmLHk6ZSxyOmcsbTpiLHM6ZHx8Zy81LGM6aHx8IiMwOGIifX1mdW5jdGlvbiB5KGYpe3E9Zi5wYWdlWDtyPWYucGFnZVl9dmFyIHY9cC53aWR0aCx3PXAuaGVpZ2h0LGw9W3Uodi8yLHcvMiwyMCwwLDIwMCwiI2ZjMyIpLHUodi8yLHcvMisyMDAsOCx7eDoxLHk6MH0pXSxtLHEscixuO3Aub25jbGljaz1mdW5jdGlvbihmKXt5KGYpO20/KG0ubT17eDoocS1tLngpLzk5LHk6KHItbS55KS85OX0sbC5wdXNoKG0pLG09MCk6bT11KHEsciw1Kk1hdGgucmFuZG9tKCkrNSl9O3Aub25tb3VzZW1vdmU9eTtzZXRJbnRlcnZhbChmdW5jdGlvbigpe3ZhciBmPWwubGVuZ3RoLGUsZyxiLGQsaDtmb3IoZT1mO2UtLTspZm9yKGI9DQpsW2VdLGc9ZjtnLS07KWQ9bFtnXSxiLnImJmQuciYmZS1nJiYoeChkLGIpLGg9Yi5zKmQucy8obipuKSx0KGIubSx7eDooZC54LWIueCkvbipoLHk6KGQueS1iLnkpL24qaH0pKTtmb3IoZT1mO2UtLTspdChsW2VdLGxbZV0ubSk7Zm9yKGU9ZjtlLS07KWZvcihiPWxbZV0sZz1mO2ctLSA+ZSsxOylkPWxbZ10seChkLGIpLGIuciYmZC5yJiZuPGIucitkLnImJihoPWIuczxkLnM/YjpkLGQ9aD09Yj9kOmIsZC5yKz1oLnIvMixkLnMrPWgucy8yLHQoZC5tLGgubSksZC5tJiYoZC5tLngvPTIsZC5tLnkvPTIpLGgucj0wKTtrLmZpbGxTdHlsZT0iIzIyMiI7ay5maWxsUmVjdCgwLDAsdix3KTtmb3IoZT1mKzE7ZS0tOykoYj1sW2VdfHxtKSYmYi5yJiYoay5iZWdpblBhdGgoKSxrLmZpbGxTdHlsZT1iLmMsay5hcmMoYi54LGIueSxiLnIsMCw3KSxrLmZpbGwoKSxiPT1tJiYoay5zdHJva2VTdHlsZT1iLmMsay5tb3ZlVG8oYi54LGIueSksay5saW5lVG8ocSxyKSxrLnN0cm9rZSgpKSl9LDEwKX0NClMoYSxjKTs=
function S (canvas, context)
{
function Coord (x, y)
{
return { x : x, y : y };
}
function Add (a, b)
{
if (a && b) {
a.x += b.x;
a.y += b.y;
}
}
function CalculateDistance (a, b)
{
var aDiff = a.x - b.x, bDiff = a.y - b.y;
distance = Math.sqrt (aDiff * aDiff + bDiff * bDiff);
}
function Planet (xPosition, yPosition, radius, motion, mass, color)
{
return {
x : xPosition, // x position of the planet
y : yPosition, // y position of the planet
r : radius, // radius of the planet, 0 if planet is not alive anymore
m : motion, // self motion of the planet
s : mass || radius / 5, // mass of the planet for gravity calculation
c : color || '#08b' // color of the planet
}
}
function UpdateMouseCoordinates (ev)
{
mouseX = ev.pageX;
mouseY = ev.pageY;
}
var width = canvas.width,
height = canvas.height,
planets = [
Planet (width / 2, height / 2, 20, 0, 200, '#fc3'),
Planet (width / 2, height / 2 + 200, 8, Coord (1, 0))
],
edited, mouseX, mouseY, distance;
// test
//planets = [
// Planet (100, 100, 10, Coord (1.5, 0), 10),
// Planet (600, 100, 10, Coord (-1.5, 0), 10)
//];
canvas.onclick = function (ev) {
UpdateMouseCoordinates (ev);
if (edited) {
edited.m = Coord ((mouseX - edited.x) / 99, (mouseY - edited.y) / 99);
planets.push (edited);
edited = 0;
} else {
edited = Planet (mouseX, mouseY, Math.random () * 5 + 5);
}
}
canvas.onmousemove = UpdateMouseCoordinates;
setInterval (function () {
var n = planets.length, i, j, aPlanet, bPlanet,
speed, absorber, absorbed;
// calculate the new motion vector for all planets
for (i = n; i--;) {
aPlanet = planets[i];
// add gravitational movement
for (j = n; j--;) {
bPlanet = planets[j];
if (aPlanet.r && bPlanet.r && i - j) { // i != j
CalculateDistance (bPlanet, aPlanet);
speed = (aPlanet.s * bPlanet.s) / (distance * distance);
Add (aPlanet.m, Coord ((bPlanet.x - aPlanet.x) / distance * speed, (bPlanet.y - aPlanet.y) / distance * speed));
}
}
}
// move all planets with the calculated motion
for (i = n; i--;) {
Add (planets[i], planets[i].m);
}
// handle planet collisions and delete absorbed planets
for (i = n; i--;) {
aPlanet = planets[i];
for (j = n; j-- > i + 1;) {
bPlanet = planets[j];
// absorb happens when the planets are too close to each other
CalculateDistance (bPlanet, aPlanet);
if (aPlanet.r && bPlanet.r && distance < aPlanet.r + bPlanet.r) {
// the absorved planet is the one with smaller mass
absorbed = (aPlanet.s < bPlanet.s ? aPlanet : bPlanet);
absorber = (absorbed == aPlanet ? bPlanet : aPlanet);
// increase the absorber planets radius and mass
absorber.r += absorbed.r / 2;
absorber.s += absorbed.s / 2;
// the new motion is the average of the two original motions
Add (absorber.m, absorbed.m);
if (absorber.m) {
absorber.m.x /= 2;
absorber.m.y /= 2;
}
// the absorbed planet is now dead
absorbed.r = 0;
}
}
}
// now draw all the planets
context.fillStyle = '#222';
context.fillRect (0, 0, width, height);
// index is out of range in case of deleted planets, but it works,
// although the edited planet sometimes drawn twice
for (i = n + 1; i--;) {
aPlanet = planets[i] || edited;
if (aPlanet && aPlanet.r) {
context.beginPath ();
context.fillStyle = aPlanet.c;
context.arc (aPlanet.x, aPlanet.y, aPlanet.r, 0, 7 /* 2 * Math.PI :) */);
context.fill ();
if (aPlanet == edited) {
context.strokeStyle = aPlanet.c;
context.moveTo (aPlanet.x, aPlanet.y);
context.lineTo (mouseX, mouseY);
context.stroke ();
}
}
}
}, 10);
}
S (a, c);