/* GravityBounce Billiard balls that bounce off walls and exhibit gravitational attraction at the same time. Brighter balls have heavier mass. By Erik Nelson (c) Oct. 3, 2004 This variation puts a sun in the center of the frame. Sun attracts but does not move. In this version each ball is initialized in a direction perpendicular to the direction between it and the sun, because stable orbits are more likely to result that way. Planets bounce off walls, just like a real planet would if there were such a wall outside the solar system. They are attracted to one another by gravitational forces according to Newton's law. (force is inversely proportional to distance and directly proportional to product of masses) Color is proportional to mass, with the lightest in black and the heaviest in orange. The bounce is "damped" so that balls go a little slower with each successive bounce. If a planet collides with the sun, it will fly off fast in a random direction. I am not sure why. This may not be realistic, but I will keep it in, because it will tend to re-randomize an otherwise boring situation. If you observe long enough you will see stable orbits, and occasionally planets orbiting other planets. If you don't get stable orbits, wait a while, or try running it again. Hit any key to clear the screen. If you want to save a screenshot of an interesting situation, de-select the window first to avoid clearing the screen when you hit the key combination. */ Ball[] balls; // This is a class of billiard-ball "planets" int winx = 467; // x and y dimensions of window If you change this, you mut repeat same numbers in "size()" function. // If it's square, you get annoying repeats. int winy = 457; int numBalls = 10; int flashcount = 0; void setup( ) { ellipseMode(CENTER_DIAMETER); size( 467 , 457 ); //same number as winx and winy. Must be written as a number here so the applet knows what size to build itself at balls = new Ball[numBalls]; float x, y, norm; for (int i=0; i=0; i--){ //let's count backwards so heavier (brighter) balls aren't obscured by lighter (darker) balls balls[i].update(); balls[i].show(); mutualforces(); } } class Ball{ float xpos, ypos, xspeed, yspeed, mass; Ball( float xcoord, float ycoord, float xvel, float yvel, float mass){ //This part is how to initialize a ball. xpos = xcoord; ypos = ycoord; xspeed = xvel; yspeed = yvel; } void update(){ //this part is how to move a ball xpos = xpos + xspeed; ypos = ypos + yspeed; // fixed the hitting-the-wall cases to make sure ball never "runs away" and damps it if ( xpos < 0 ) { xspeed = 0.75 * abs(xspeed); }; if ( xpos > winx ) { xspeed = -0.75 * abs(xspeed);}; if ( ypos < 0 ) { yspeed = 0.75 *abs(yspeed);}; if (ypos > winy){ yspeed = -0.75 * abs(yspeed);}; } void show(){ // this part is how to draw a ball fill(2*mass, 0.5 * mass,50 +0.1 * mass); ellipse(xpos, ypos, 6, 6 ); } } void mutualforces() { // Enact gravity-like forces between balls. int i, j; float gravfactor = 0.004; float accelsumx; float accelsumy; float thisdistance2; // "this distance squared" square of distance btw ball[i] and ball[j] to avoid square root. int xsign; int ysign; // whether the respective distance components are in a pos or neg direction, for ( i=numBalls-1;i>=0; i--){ //let's count backwards to add small numbers first, maintaining accuracy accelsumx=0; accelsumy=0; for(j=numBalls-1; j>=0; j--){ if(i != j){ //You can't measure gravity between a thing and itself! thisdistance2 =( sq(balls[i].xpos - balls[j]. xpos) ) +( sq(balls[i].ypos - balls[j].ypos)); if (abs(thisdistance2)< .1) { thisdistance2 = .1; }; // to avoid divides by near-zero if ( balls[i].xpos > balls[j].xpos ) { xsign = -1 ; } else { xsign = +1 ;}; if ( balls[i].ypos > balls[j].ypos ) { ysign = -1 ;} else { ysign = +1 ;}; accelsumx +=( balls[j].mass * xsign)/thisdistance2; accelsumy +=( balls[j].mass * ysign)/thisdistance2; if( i == 0 ) { balls[i].xspeed = balls[i].yspeed = 0;}; //don't move balls[0] which is the sun // a = f/m for balls[i] and f = constant times product of balls[i].mass and balls[j].mass divided by distancesquared // So balls[i].mass cancels out of the equation (which I forgot in earlier version!) // when we compute change in speed above // Maybe it's inefficient to calculate each force twice (once for each object in the pair) but never mind. // For some reason the balls speed up hyperactively then become zero and everything stands stil in 0,0 corner // in earlier versions. I suspect that // this is due to numerical overflow of floating point numbers. So I will endeavor to fix this by the following lines: // not really physically correct but keeps the simulation from "overheating" if ( balls[i].xspeed > 20 ) { balls[i].xspeed = 0; balls[i].yspeed = 0;} ; if ( balls[i].yspeed > 20 ) { balls[i].xspeed = 0; balls[i].yspeed = 0;} ; if ( balls[i].xspeed < -20 ) { balls[i].xspeed = 0; balls[i].yspeed = 0;} ; if ( balls[i].yspeed < -20 ) { balls[i].xspeed = 0; balls[i].yspeed = 0;} ; } //close ifclause }//close j for balls[i].xspeed += gravfactor * accelsumx; balls[i].yspeed += gravfactor * accelsumy; } //close i for }// close definition void keyPressed() { background(0,0,100); }