If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

Mutual attraction

Hopefully, you found it helpful that we started with a simple scenario—one object attracts another object—and moved on to one object attracts many objects. However, it’s likely that you are going to find yourself in a slightly more complex situation: many objects attract each other. In other words, every object in a given system attracts every other object in that system (except for itself).
We’ve really done almost all of the work for this already. Let’s consider a program with an array of Mover objects:
var movers = [];

for (var i = 0; i < 5; i++) {
    movers[i] = new Mover(
        random(0.1, 2),
        random(width),
        random(height));
}

draw = function() {
    background(255, 255, 255);
    for (var i = 0; i < movers.length; i++) {
        movers[i].update();
        movers[i].display();
    }
};
The draw() function is where we need to work some magic. Currently, we’re saying: “for every mover i, update and display yourself.” Now what we need to say is: “for every mover i, be attracted to every other mover j, and update and display yourself.”
for (var i = 0; i < movers.length; i++) {
    // For every Mover, check every Mover!
    for (var j = 0; j < movers.length; j++) {
        var force = movers[j].calculateAttraction(movers[i]);
        movers[i].applyForce(force);
    }
}

for (var i = 0; i < movers.length; i++) {
    movers[i].update();
    movers[i].display();
}
Notice that we made an entirely new for loop above the update-and-display loop. That's because we want to make sure that we calculate all the attraction forces before updating each mover. If we accidentally updated each mover in that first for loop, then we'd be affecting the calculation of the attraction forces after, and they wouldn't be accurate.
Our code won't work yet, because we need each Mover to have a calculateAttraction() method. In the previous example, we had an Attractor object with a method named calculateAttraction(). Now, since we have movers attracting movers, we can copy that method into the Mover object:
Mover.prototype.calculateAttraction = function(m) {
  var force = PVector.sub(this.position, m.position);
  var distance = force.mag();
  distance = constrain(distance, 5.0, 25.0);                        
  force.normalize();

  var strength = (G * this.mass * m.mass) / (distance * distance);
  force.mult(strength);
  return force;
};
Of course, there’s one small problem. When we are looking at every mover i and every mover j, are we OK with the times that i equals j? For example, should mover #3 attract mover #3? The answer, of course, is no. If there are five objects, we only want mover #3 to attract 0, 1, 2, and 4, skipping itself. We do, however, want to calculate and apply both the force from mover #3 on mover #1, and mover #1 on mover #3. The calculated forces will be the same for the pair, but the resulting acceleration will be different, depending on the mass of each mover. Our attraction table should look like:
0 ⇢ 1, 2, 3, 4
1 ⇢ 0, 2, 3, 4
2 ⇢ 0, 1, 3, 4
3 ⇢ 0, 1, 2, 4
And so, we finish this example by modifying our for loop so that the inner loop avoids movers attracting themselves:
for (var i = 0; i < movers.length; i++) {
    for (var j = 0; j < movers.length; j++) {
       if (i !== j) {
         var force = movers[j].calculateAttraction(movers[i]);
         movers[i].applyForce(force);
       }
    }
}
Let's see it all together now:

This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.

Want to join the conversation?

  • aqualine ultimate style avatar for user Bala
    Oh my god, I solved this challenge by typing one character!
    (9 votes)
    Default Khan Academy avatar avatar for user
  • piceratops ultimate style avatar for user cody.codes
    Hello! Can anyone help guide me in the right direction as to what I should be looking for to fill in these blanks:

    Mover.prototype.calculateAttraction = function(m) {
    ____;
    var strength = ____;
    ____;
    };
    ? I'm pretty sure I got the force.mult(-1*strength); part right, but I don't know where to look to fill in these blanks from the hint! Thanks :).
    (3 votes)
    Default Khan Academy avatar avatar for user
    • leaf green style avatar for user appinv
      You need to put a minus sign before the G (well, as we are dealing with vectors, putting a minus changes the direction, putting a minus before G changes the answer for the force from positive to negative, from attraction to repulsion)
      (5 votes)
  • orange juice squid orange style avatar for user Igor Vínicius
    why in the line 30 ( Mover.prototype.calculateAttraction = function(m,i) {) the function is accepting two parameters ? i see the m used inside the function, but i not see the i being used :/
    (5 votes)
    Default Khan Academy avatar avatar for user
  • marcimus pink style avatar for user cristina.hernando
    Is it important the order in the methods, when creating the object? I mean at the beggining of the code, not at the draw function. I've realized the .display is usually after the .applyForce and the updatemethods.
    (2 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user Grady Welsh
    Is it possible for an object to be attracted to another object (like in the gravity examples) when it is within a certain distance, but when it is outside of that distance, it moves randomly (like in the walker examples)?
    I've tried using PVector.sub to calculate the distance but I'm not sure where I would put the if function...
    for example:

    if (within certain distance) {
    calculate attraction}
    else {
    move randomly}

    here is my actual code that I tried putting within the draw function:
    (I know the problem is in this part because both the attraction and random movement work when used individually)

    for (var i = 0; i < movers.length; i++) {
    var force = attractor.calculateAttraction(movers[i]);
    if (force<100) {
    movers[i].applyForce(force);
    } else {
    movers[i].update();
    }
    movers[i].checkEdges();
    movers[i].display();
    }

    any tips?
    thanks
    (3 votes)
    Default Khan Academy avatar avatar for user
  • mr pink red style avatar for user Bob Everton
    In this last example on mutual attraction there are three loops, one outside the draw function and two inside. I'm getting lost following the logic. Could someone give me an explanation foor each.
    Thank you.
    (1 vote)
    Default Khan Academy avatar avatar for user
    • spunky sam blue style avatar for user Dalendrion
      First, you make a bunch of Movers. 5 of them in fact.
      You want to make them only once, otherwise you'll end up with thousands. :P So you do that outside the draw function.

      Then, each Mover is attracted to each other mover.
      You could draw a table:
        | 0  1  2  3  4
      --+--------------
      0 | x o o o o
      1 | o x o o o
      2 | o o x o o
      3 | o o o x o
      4 | o o o o x
      Where there's an o, the two movers attract each other.
      So for each Mover in in the left column, you find the attraction for each Mover in the upper row: two nested for loops.
      (4 votes)
  • hopper cool style avatar for user yufang cao
    Just put a minus sign before the 1 and after the = on line 1
    (2 votes)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user yesungsohn
    What differentiates ==! and ===?
    (1 vote)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user João Augusto
    Hiii, how can i erase some of my spin-offs, i tried to find something like that and there's nothing
    (1 vote)
    Default Khan Academy avatar avatar for user
  • hopper cool style avatar for user Noah
    In the next challenge, why is it so easy? It is not making me follow the Hints, and all I did was multiply the force by -1 and I passed. Is that what I was supposed to do?
    (2 votes)
    Default Khan Academy avatar avatar for user