how to code gravity for ellipses

edited June 2015 in How To...

hi,

i want to create a simple program. ellipses will spawn randomly in the screen. i want each ellipse to have a gravitational force so it could be a simulation of celestial spheres.

how can i code this gravitational force?

Answers

  • edited June 2015

    Develop your sketch one step at a time. First, a simple program.

    void setup(){
      size(600,600);
    }
    
    void draw(){
      background(0);
    }
    

    Doesn't get much more simple. And we can be sure that that works.

  • edited June 2015

    Now we need to add ellipses that spawn randomly. But we don't just want to draw them. We want to remember where they are (because we'll need to know their positions later, and change them too, right?). Let's start with just one of them.

    PVector pos;
    
    void setup(){
      size(600,600);
      pos = new PVector( random(width), random(height), 0 );
    }
    
    void draw(){
      background(0);
      ellipse(pos.x, pos.y,20,20);
    }
    

    Great. We now have a randomly placed ellipse.

  • edited June 2015 Answer ✓

    Now this is an important step. We make our ellipse into its own object.

    class Dot {
      PVector pos;  
      Dot() {
        pos = new PVector( random(width), random(height), 0 );
      }
      void draw() {
        ellipse(pos.x, pos.y, 20, 20);
      }
    }
    
    Dot dot;
    
    void setup() {
      size(600, 600);
      dot = new Dot();
    }
    
    void draw() {
      background(0);
      dot.draw();
    }
    

    Notice that it works the same as before. So why bother with this step?

  • This is why. It's so we can make many copies of that one dot object easily, without needing to make different arrays for each variable that each dot needs to remember.

    class Dot {
      PVector pos;
      int id;
      Dot(int iid) {
        id = iid;
        pos = new PVector( random(width), random(height), 0 );
      }
      void draw() {
        ellipse(pos.x, pos.y, 20, 20);
      }
    }
    
    ArrayList<Dot> dots;
    
    void setup() {
      size(600, 600);
      dots = new ArrayList();
      for (int i=0; i<200; dots.add (new Dot (i++)));
    }
    
    void draw() {
      background(0);
      for (int i=0; i<dots.size (); dots.get(i++).draw());
    }
    

    Also notice that we added and id field for each dot, so each dot knows which numbered dot itself is. This will be handy later.

  • edited June 2015 Answer ✓

    See? In this step we add a color to each dot, and we do it by adding a new color variable to our Dot object (instead of having to add a whole separate array of colors to track this). We also break the draw() method into simulate() and render() steps.

    class Dot {
      PVector pos;
      int id;
      color c;
      Dot(int iid) {
        id = iid;
        pos = new PVector( random(width), random(height), 0 );
        c = color(random(255), random(255), random(255));
      }
      void draw() {
        simulate();
        render();
      }
      void simulate() {
      }
      void render() {
        fill(c);
        ellipse(pos.x, pos.y, 20, 20);
      }
    }
    
    ArrayList<Dot> dots;
    
    void setup() {
      size(600, 600);
      dots = new ArrayList();
      for (int i=0; i<200; dots.add (new Dot (i++)));
    }
    
    void draw() {
      background(0);
      for (int i=0; i<dots.size (); dots.get(i++).draw());
    }
    

    Before we can even start to apply different forced to each ellipse, we need to make them move. That comes next.

  • you made a custom class then an array list for it, then functions. very systematic and educative.

    right now i cant totally grasp arrays and classes but i ll study your entries again tomorrow, with re writing your code and checking references .

    thanks so much and please continue :)

  • edited June 2015 Answer ✓

    So what's involved with adding movement? Not much! Instead of doing a lot of crazy looping over many arrays, we only need to add the new behavior to our Dot class. We add a velocity vector, make it point in a random direction, and then add it to that dot's position vector. And now all the dots move!

    class Dot {
      PVector pos, vel;
      int id;
      color c;
      Dot(int iid) {
        id = iid;
        pos = new PVector( random(width), random(height), 0 );
        vel = new PVector( random(-5,5), random(-5,5), 0);
        c = color(random(255), random(255), random(255));
      }
      void draw() {
        simulate();
        render();
      }
      void simulate() {
        pos.add(vel);
      }
      void render() {
        fill(c);
        ellipse(pos.x, pos.y, 20, 20);
      }
    }
    
    ArrayList<Dot> dots;
    
    void setup() {
      size(600, 600);
      dots = new ArrayList();
      for (int i=0; i<200; dots.add (new Dot (i++)));
    }
    
    void draw() {
      background(0);
      for (int i=0; i<dots.size (); dots.get(i++).draw());
    }
    

    We have movement! Now we just need to make the movement based on the positions of other ellipses instead of just being random.

  • edited June 2015

    this is creating dots =>

    for (int i=0; i<200; dots.add (new Dot (i++)));

    but what is this doing?=> (specially, what does ".size" mean?)

    for (int i=0; i<dots.size (); dots.get(i++).draw());

  • Remove the initial random velocity, and add some logic to loop over all the dots, setting their accelerations, and...

    class Dot {
      PVector pos, vel, acc;
      int id;
      color c;
      Dot(int iid) {
        id = iid;
        pos = new PVector( random(width), random(height), 0 );
        vel = new PVector( 0, 0, 0 );
        acc = new PVector( 0, 0, 0 );
        c = color(random(255), random(255), random(255));
      }
      void set_acc() {
        for( int i = id; i< dots.size(); i++){
          if( i != id ){
            PVector t = new PVector(
              dots.get(i).pos.x - pos.x,
              dots.get(i).pos.y - pos.y,
              0
            );
            float dis = dist(pos.x,pos.y,dots.get(i).pos.x,dots.get(i).pos.y);
            t.div(dis);
            t.div(dis);
            acc.add(t);
            t.mult(-1);
            dots.get(i).acc.add(t);
          }
        }
      }
      void draw() {
        simulate();
        render();
      }
      void simulate() {
        vel.add(acc);
        pos.add(vel);
        acc.x = 0;
        acc.y = 0;
      }
      void render() {
        fill(c);
        ellipse(pos.x, pos.y, 20, 20);
      }
    }
    
    ArrayList<Dot> dots;
    int num_dots = 100;
    
    void setup() {
      size(600, 600);
      dots = new ArrayList();
      for (int i=0; i<num_dots; dots.add (new Dot (i++)));
    }
    
    void draw() {
      background(0);
      for (int i=0; i<dots.size (); dots.get(i++).set_acc());
      for (int i=0; i<dots.size (); dots.get(i++).draw());
    }
    

    Magic.

  • You need to use .size() when working with ArrayLists, because the ArrayList can store objects of different types. It is similar to Array.length, which tells you how many objects are in the Array. It returns how may objects are stored in the ArrayList.

  • edited June 2015

    can you briefly tell the physics logic behind it? what is happening here :)

                 void set_acc() {
                        for( int i = id; i< dots.size(); i++){
                          if( i != id ){
                            PVector t = new PVector(
                              dots.get(i).pos.x - pos.x,
                              dots.get(i).pos.y - pos.y,
                              0
                            );
                            float dis = dist(pos.x,pos.y,dots.get(i).pos.x,dots.get(i).pos.y);
                            t.div(dis);
                            t.div(dis);
                            acc.add(t);
                            t.mult(-1);
                            dots.get(i).acc.add(t);
    
  • Answer ✓

    For every dot in the ArrayList from myself onwards,

    If that dot is not me,

    Find a vector, t, that is the difference from that dot's position and my position. This is the first step towards finding how much force that Dot pulls on me.

    Find the distance between that other dot and myself.

    Divide the vector found by the distance twice, so that the force between us is proportional to the square of the distance between us (like real gravity!).

    Add this force to my acceleration.

    Add the negative of that force to the other dot's acceleration.


    Notice that each dot's acceleration vector is zeroed-out after the position is updated in simulate().

    Also notice that since the "other dot" always appears later in the Array List, that "other dot" won't re-add this force again, as it will only add more forces to itself and dots that come after it.

  • Answer ✓

    I lied. The first t.div(dis); normalizes the force. The second one makes it proportional to the distance. If you add a THIRD t.div(dis);, then THAT'S like real gravity.

  • i tried adding the third t.div(dis), its running normally, looking like gravity but the motion is too slow, what could be the reason?

  • edited June 2015

    No gravitational constant is being used. Well, it is, but it's implied to be 1.

    For bonus points, make masses with different sizes and make more massive ellipses attract more!

    F = G * m * M / d / d

  • Hi TFGuy44, I have been doing this in java. I would like to use the gravitational constant which I set to 6.673e-11. I tried using the force formula as double strength = (G * mass * particles.get(i).getMass()) / (dis * dis); But I am unsure as to what I should apply this to and how in the set_acceleration function.

    Mine so far looks like this in java:

    public void setAcceleration() { for(int i = id; i < particles.size(); i++) { if(i != id){ Vector vec = new Vector(particles.get(i).getPos().getX() - pos.getX() ,particles.get(i).getPos().getY() - pos.getY()); double dis = Vector.distance(pos, particles.get(i).getPos()); double force = (G * mass * particles.get(i).getMass()) / (dis * dis); vec.divide(dis); vec.divide(dis); vec.divide(dis); acc.add(vec); vec.multiply(-1); particles.get(i).getAcceleration().add(vec); } } }

  • First, let's clarify something: The "acceleration" this is creating is NOT acceleration - it's the resulting FORCE that should be applied to this Dot based on all the other Dots. Since my original dots all had the same mass of "one mass", F = ma, or F = a if m is 1.

    But no more! Let's have dots with mass!

    class Dot {
      PVector pos, vel, acc;
      float mass;
      int id;
      color c;
      Dot(int iid) {
        id = iid;
        pos = new PVector( random(width), random(height), 0 );
        vel = new PVector( 0, 0, 0 );
        acc = new PVector( 0, 0, 0 );
        mass = random(3,100);
        c = color(random(255), random(255), random(255));
      }
      void set_acc() {
        for( int i = id; i< dots.size(); i++){
          if( i != id ){
            PVector t = new PVector(
              dots.get(i).pos.x - pos.x,
              dots.get(i).pos.y - pos.y,
              0
            );
            float dis = dist(pos.x,pos.y,dots.get(i).pos.x,dots.get(i).pos.y);
            t.div(dis);
            // t is now a "unit vecctor" in the direction this force will add.
            t.mult(mass); // 1 * m
            t.mult(dots.get(i).mass); // m * M
            t.mult(float(1)); // m * M * G (G is still 1)
            t.div(dis);
            t.div(dis); // G * m * M / d / d
            acc.add(t);
            t.mult(-1); // Pulls in opposite direction for other Dot.
            dots.get(i).acc.add(t);
          }
        }
      }
      void draw() {
        simulate();
        render();
      }
      void simulate() {
        acc.div(mass); // Since F = m * a, a = F / m
        vel.add(acc);
        pos.add(vel);
        acc.x = 0;
        acc.y = 0;
      }
      void render() {
        fill(c);
        ellipse(pos.x, pos.y, mass, mass); // Size is now indication of mass.
      }
    }
    
    ArrayList<Dot> dots;
    int num_dots = 20;
    
    void setup() {
      size(600, 600);
      dots = new ArrayList();
      for (int i=0; i<num_dots; dots.add (new Dot (i++)));
    }
    
    void draw() {
      background(0);
      for (int i=0; i<dots.size (); dots.get(i++).set_acc());
      for (int i=0; i<dots.size (); dots.get(i++).draw());
    }
    
  • This looks about right, if you ask me. Large planets can slingshot smaller ones off at great speeds if they get close to the center. Of course, with real masses, this would be a whole lot more collision-y. (Bonus points: Change the code so masses that collide stuck to each other!)

    You may also wonder why I kept G at 1, instead of 0.00000000006673.

    This is because my masses range from 3 to 100, which are much, much smaller than "planetary" masses that you would be using in Physics. Basically, the "large" value I use for G cancels our the "small" value I use for planet masses.

  • TfGuy44 it works! I had no idea that formula for the force had to be applied to the new vector in that way! I've never had a good physics background but I do love astronomy and I've been wanting to create some kind of solar system simulation, and I am definitely aiming at the next step of having the particles combine! But this gravity simulating part had been kicking my butt! I tried using the G constant and you were right it was making objects barely move because the mass values I've been using were too small. Thank you so much!

  • I've been thinking about which approach I want to take for combining the particles/dots.

    First and simpler approach which I did first is to each dot to check if its thin the area of another dot, then the more massive dot gets the mass of the smaller dot added to it and I remove the smaller dot from the arrayList. In turn I made the dot's size depend on its mass.

    A second approach I find more interesting to think about but a bit more complex, that would be to not get rid of the smaller dot but to keep it attached to the more massive one, maybe by setting the smaller dot's velocity to zero upon impact and then set its velocity to the bigger dot's?

    I am sure in a more realistic simulation I would have to take account for other forces like inertia for example. Any thoughts on this?

Sign In or Register to comment.