[CHIME]
It is time for Newton's law of universal gravitation.
So this is the final example that I will be demonstrating
as part of chapter 2, although the concept of forces
and how they are applied permeate throughout the rest
of the videos that I intend to make.
But I'm going to wrap up this chapter
by looking at the equation for universal gravitation
that looks exactly like this and trying
to see can I take this equation and implement it
in a P5JS sketch.
Here's the same formula with a diagram in the Nature of Code
book itself.
So the idea of this law is that everybody, every particle,
every thing in the universe attracts every other thing
with a force that's proportional to the masses of those bodies
and inversely proportional to how far apart they are.
So the farther apart they are, the weaker the force.
The closer they are, the stronger the force.
I did something in the first example in the series very
similar where I took a mover object
and created a vector that pointed from that mover
object to the mouse itself and set that vector
as the acceleration of the object.
So this was like attraction but I wasn't thinking about math.
And I wasn't thinking about distance.
Let me diagram what I want to do in this video.
So I'm going to simplify things and have this concept
of an attractor object.
So the attractor object isn't a real body in the universe.
It's a fixed entity that attracts things to it,
but it cannot move.
And it cannot experience forces itself.
Then I'm going to have the mover object with some given
velocity.
And the mover object is going to experience
a force, a gravitational force, that pulls it to the attractor.
And that force, which is a vector,
is going to be equal to the mass of one object times the mass
of the other object divided by the distance squared.
And I put this in the wrong order
but also times G, which is the universal gravitational
constant.
Now there's a very real number that
is this value in the universe as we know it.
But for us in our pretend P5 canvas,
if I draw like a box around this,
the G is just a way for us to kind of tune
the world to have stronger gravitational attraction
or weaker attraction forces.
And in a way, I could think of this G
as a property of the attractor itself.
Maybe different attractors have different constants,
although, to some extent, that is taken care of by the mass.
So I need everything that's in this formula--
I need to figure out where is it here.
So the mass of one object, well, it
would be the mass of this mover object.
The mass of the other object would
be the mass of the attractor.
So to some extent, the mass in G,
I could conflate these into one constant.
But I'll keep them separate for now.
Distance is the distance between the centers of both objects.
Remember when I'm calculating a force, I need two things.
I need direction and I need magnitude.
So this is really all of these numbers are
talking about the magnitude.
How strong is that force?
How long is this arrow that I'm drawing here?
The direction is just a vector that
points from the object being attracted to the attractor.
And so that I can get-- if you remember
I've done this a bunch of times now-- with subtraction.
So the sub function for subtraction
I can take the position of the attractor minus the position
of the mover and that gives me a vector that points this way.
Set its magnitude to the result of this formula.
And voila, I have gravitational attraction.
So coming back to this example, I
am going to now get rid of this acceleration calculation.
And when I run the sketch, I should just see this mover
not moving at all.
And in the main JavaScript file where I have set up a draw,
I want to add an attractor.
So I'll create an attractor that also
gets an xy position and a mass.
I'll say attractor.attract(mover)
and attractor.show.
Now I might have just confused you for a second
there because you might be thinking, wait,
did I miss something?
Did you write an attractor class?
And is this just going to work right now?
Definitely not.
Like when I run this, it's going to be like,
I have no idea what that is.
So this is the way that I work sometimes.
And I was a little bit of flawed in making this tutorial video
because this is probably an example that I've written many,
many times.
So I had it just sort of right in my mind at the ready.
But sometimes it can be helpful to sort of sketch out
how it is you imagine writing the code
to operate an object before writing the class,
because now I have the instructions for what
it is I need to write.
I need to make a class called attractor.
The constructor takes three arguments.
It needs to have a show function.
And it also needs to have an attract function that
receives a mover object.
So all of these things are now at things
that I can write into the attractor class.
I've already created another JavaScript file, attractor.js,
which is empty.
So I'll put class attractor in there,
then write the constructor, which if you remember
required three arguments, an x, y, and a mass.
The x y I'll use for position.
The m I'll use for mass.
Let me write a show function just to draw it as a circle.
I'm now realizing I should calculate a radius based
on mass the same way that I did in the previous examples
with the mover.
So I'm just going to grab this code, which takes the mass,
takes the square root, and multiplies by 10.
I'll use the same convention.
So let's leave out the attract function for a moment.
Comment that out.
Move the mover to like a little higher up in the canvas.
Run it.
And what do I have now?
Attractor is not defined.
Ooh.
I can never seem to remember to add the script tag when
I have a new JavaScript file.
And here we go.
All right.
Now I have the mover not yet moving
because no force has been activated
and the attractor just in the canvas.
So the next step is for me to write this attract function
and implement the formula.
Before I do so, little let me address something.
Why did I choose to say attractor.attract(mover)?
I could have called a function on mover instead of attractor,
or I could have written a new function that
takes the two arguments, the mover and attractor,
or I could just work out the code in here.
So I'm making a somewhat arbitrary choice.
But to me, it feels conceptually consistent to have
the attractor object itself have the functionality
to create a force that attracts the mover object.
But if you want to try to redo it and reorganize
your code in another way, I would really
encourage you to do that and share it with me.
Now I did make a sort of small error
here or inconsistency whereas that I'm
saying I'm calculating a vector but all
of the variables and aspects of the formula that I'm
working with here are just calculating a scalar value,
the magnitude of that vector.
So let me be a little bit more clear about this diagram.
If I have these two bodies each with a mass map,
this one being the attractor but that doesn't really
matter so much, and I think of this vector pointing
from object one to object two as a vector r,
then the distance is really the magnitude
of that vector squared.
And then the direction is the unit vector r--
which I don't really have a lot of room here--
which I could write as r with the hat on it.
So now this formula is more accurate.
And all I need to do is calculate
both-- in my code is calculate both this unit
vector and this equation, put them together, and apply
that force.
And I'm going to do all of this in a new function called
attract.
That's part of the attractor.
So let me first get this vector r.
And I'll call it direction.
It is this position minus the mover's position.
We need to say subtract.
The distance is the magnitude of that vector.
And actually, I'm going to want distance squared.
So let's make this distance squared is magnitude squared.
Going to make up just for right now
a universal gravitational constant of 1.
So I could really just ignore it,
but let's keep the variable in there
and just set it equal to 1.
And I'm going to use the word strength to calculate
the magnitude of that force, which is this.mass times
mover.mass--
I'm making myself more room here--
divided by distance squared and also G times that.
So this right here is exactly the same formula
that I have right over here.
And I just want to take this vector r
and set its magnitude to the result of this.
Just to be consistent, I'm actually
going to rename this from direction to force.
I'm going to use this variable to be the force.
And then I'll say force.set magnitude to that strength.
And then mover.applyforce(force).
And we can see I have all the steps here.
Get a vector that points from the mover to the attractor.
Calculate all of the elements to get
the magnitude of that vector.
Put it in this variable strength.
And then set the magnitude of that vector to strength
and apply the force.
I don't know.
Let's run this.
Is it going to work?
I think so.
I'm not seeing anything.
OK.
One thing would be to actually call the function attract.
Stength is not defined.
All right.
That must be a mistake that you've all noticed.
Strength with an R. Oh wait, I is moving just ever
so slightly and slowly.
This is where having G can really come in handy.
Let's make G 5.
And let's make G 50.
So I can scale the force relative
to that universal gravitational constant.
But there's actually something else
going on here that I think that I would like to fudge.
Well, I really would love to stay so true to this formula.
You are a wonderful formula.
And I want to stay true to that formula.
Give me a little bit of wiggle room here,
because this is this made up P5 world.
It's pixels.
And there's a lot of aspects to the feeling
that I want to create and how it's actually going to behave
with this literal formula.
And one of the-- a big element here that's playing a role here
is this distance squared.
So you think about it, the amount of pixels
apart that these could be could very easily be 200 pixels.
And if they're 200 pixels apart, that's
going to be dividing by 40,000, which
is going to make the strength of the force
very, very, very small.
So I could increase the mass of everything.
I could increase G. But ultimately,
something that I might just do is constrain the distance
that I'm using in the calculation to a limit.
So I'm always going to, even if it's
farther than 50 pixels away maybe, just cap that at 50.
On the other hand, when they're really, really close,
they could actually be in the same spot, in which case
the distance would be zero, which
doesn't make any sense in actual reality but could happen here,
or even if their distance is something like 0.001 pixels
apart, if I were to square this to have distance
squared and then divide by it, it
would make that strength so strong
that things could kind of spiral out of control.
So this is something that you could really play with it
and try a lot of different scenarios.
But for me right now, I'm going to say
that whatever that distance is, I
want to constrain its range when I go
and calculate the force's magnitude.
To do that, I'm just going to use the P5 constrain function.
And now I am getting the magnitude squared,
which I don't love.
So I'm going to constrain it between 25, which is 5 squared,
and 50 squared, which is 2,500.
Let's see what happens now.
There's no science to what I'm doing here, specifically
because I'm violating the actual rules of science,
but this is something that you might--
these are parameters that you may want
to play with based on what it is you really want your system
to do.
In this case, I think this would be a better demonstration
if I give that mover an initial velocity so I
am going to do that.
I'll give it a random one just so we can see different things.
And it has a bit more of the quality now of a body
orbiting this attractor.
One thing that might actually make sense
here is to get rid of this multiplier of mass
and actually just give the objects bigger mass.
So I'm going to give the objects a mass of 50 each.
Ooh.
Yikes.
And then let me just multiply it by 2.
All right.
This is what I was kind of looking to see.
And actually, it would be interesting to trace this-- one
thing visually that I think might be interesting to do
is trace the pattern.
And for that, I can give the background
quite a bit of alpha.
And we can kind of see the path that it's going in.
And then even better, let me make this an array.
[MUSIC PLAYING]
Now I'm making 10 mover objects.
Oh, and I want to give them each at a random location
with a random mass.
So I'm going to just put 10 out into the canvas.
I'll make the attractor a little bit bigger with mass.
And then I have to loop through them all in draw.
Let me give a bit more pixel space to work with here.
And let's see what this looks like.
Whoa.
What happened to the mover?
Oh, no.
OK.
Let me put the attractor in the middle.
And I think I want to draw the mover a different color
to make it more obvious.
I'm going to make this constraint of the distance even
smaller so it's still weighted according to distance
but it won't be as extreme.
And let's see what this take should look like.
So now again, what have I made here?
I don't really know.
But what I'm showing--
and actually, sorry, I want the initial velocities
to be a bit stronger I think.
We'll also make this demonstration
a little bit nicer.
There we go.
So this is what I was more hoping to see.
So what I'm looking to do here is
create a bunch of mover objects that are orbiting and swirling
around this one attractor.
So this is what it looks like without the trails.
And you can see I've kind of wrapped up this example
that I wanted to make.
And there's a lot that you could play with here.
Incidentally, this might look very familiar to you.
About three years ago, I did a coding challenge
called attraction and repulsion where I made exactly this.
The difference was I took it a bit further.
And this is something you might think about trying on your own
now with this as a basis for your example
is what if I were to click the mouse
and add multiple attractors.
What if some of those weren't actually attractors at all
but repellers.
How would I make a repulsion force?
It's actually pretty easy.
Just multiply by negative 1 to reverse the direction.
So I'm going to actually do some of that
in a follow up, which will be the next video in your playlist
here.
But what I want to look at more closely
though that neither of--
not this example or that previous coding challenge
addressed is what if I want every mover object
to exert a force on every other mover object.
What if going back to Newton's law of universal attraction,
every particle attracts every other particle?
So really there should be a force.
All of these should be attracting every other one.
And what does the system look like
if I have a lot of bodies floating around all mutually
attracting each other?
So that's what I'm going to focus on in the next video.
It's a bit of an introduction to a group behavior.
I might need like a nested loop.
We'll see.
And that'll really wrap up this chapter 2.
[MUSIC PLAYING]