Processing Physics Sim - processing

I am very new to processing and coding in general.
I am trying to make a bit of a physics sim of a ball bouncing with gravity accelerating and decelerating it.
You can see that it works reasonable well on the way down, but then after bouncing it never reaches the top again and I don't understand why. On the way down the speed should be multiplying by gravity, and on the way up it's dividing by gravity. I guess for some reason the code loops faster/more times on the way up and so the speed slows faster. In this sample I was hoping to flip the direction once it hits ypos = 0 again, however I did try instead flipping the direction once ballspeed = very slow and it did a few bounces and got lower and lower each time. Ironically, it behaved more realistically as if there was some loss of energy, but that's not what I want for now since I haven't added any such factor!
void setup(){
size(500,650);
background(0);
}
float ballspeed = (0.1);
float ypos = (20);
int direction=(1);
float gravity=(1.098);
void draw(){
background(0);
ballmove();
}
void ballmove(){
stroke(255);
noFill();
ellipse(250,ypos,50,50);
if ( direction == 1){
ballspeed = ballspeed * gravity;
ypos = ypos + ballspeed;
}
if ( direction ==- 1 ){
ballspeed = ballspeed / gravity;
ypos = ypos + (ballspeed);
}
if ( ypos > 600 ){
direction =- direction;
ballspeed =- ballspeed;
}
if ( ypos == 0 ){
ballspeed = 0.1;
direction =- direction;
}
}

It's due to the fact that what you're doing here is the Euler's integration and that it's only an approximation of the real world because the timesteps aren't infinitely short. In the real world, the speed and the positon of an object aren't updated every n milliseconds, it happens all the time so with this method, the best you can do is to reduce your timesteps but it will never be perfect.
EDIT : I just noticed that you were multiplying and dividing by the gravity. That's not how physic works: you should just subtract by gravity*timestep since the speed is the antiderivative of the acceleration (which in your case is -gravity). like that your model would be way more realistic and you wouldn't even have to make a difference between the way up and the way down.
In any case, this formula : Ypos = h - g / 2 * (time % sqrt(8 * h / g) - sqrt(2 * h / g)) ^ 2 will give the exact result even if it's not a real sim anymore.

Related

Why is my code for a double pendulum returning NaN?

When I print my acceleration and velocity, they both start (seemingly) normal. Shortly, they start getting very big, then return -Infinity, then return NaN. I have tried my best with the math/physics aspect, but my knowledge is limited, so be gentle. Any help would be appreciated.
float ang1, ang2, vel1, vel2, acc1, acc2, l1, l2, m1, m2, g;
void setup() {
background(255);
size(600, 600);
stroke(0);
strokeWeight(3);
g = 9.81;
m1 = 10;
m2 = 10;
l1 = 100;
l2 = 100;
vel1 = 0;
vel2 = 0;
acc1 = 0;
acc2 = 0;
ang1 = random(0, TWO_PI);
ang2 = random(0, TWO_PI);
}
void draw() {
pushMatrix();
background(255);
translate(width/2, height/2); // move origin
rotate(PI/2); // make 0 degrees face downward
ellipse(0, 0, 5, 5); // dot at origin
ellipse(l1*cos(ang1), l1*sin(ang1), 10, 10); // circle at m1
ellipse(l2*cos(ang2) + l1*cos(ang1), l2*sin(ang2) + l1*sin(ang1), 10, 10); // circle at m2
line(0, 0, l1*cos(ang1), l1*sin(ang1)); // arm 1
line(l1*cos(ang1), l1*sin(ang1), l2*cos(ang2) + l1*cos(ang1), l2*sin(ang2) + l1*sin(ang1)); // arm 2
float mu = 1 + (m1/m2);
acc1 = (g*(sin(ang2)*cos(ang1-ang2)-mu*sin(ang1))-(l2*vel2*vel2+l1*vel1*vel1*cos(ang1-ang2))*sin(ang1-ang2))/(l1*(mu-cos(ang1-ang2)*cos(ang1-ang2)));
acc2 = (mu*g*(sin(ang1)*cos(ang1-ang2)-sin(ang2))+(mu*l1*vel1*vel1+l2*vel2*vel2*cos(ang1-ang2))*sin(ang1-ang2))/(l2*(mu-cos(ang1-ang2)*cos(ang1-ang2)));
vel1 += acc1;
vel2 += acc2;
ang1 += vel1;
ang2 += vel2;
println(acc1, acc2, vel1, vel2);
popMatrix();
}
You haven't done anything wrong in your code, but the application of this mathematical technique is tricky.
This is a general problem with using numerical "solutions" to differential equations. Similar things happen if you try to simulate a bouncing ball:
//physics variables:
float g = 9.81;
float x = 200;
float y = 200;
float yvel = 0;
float radius = 10;
//graphing variables:
float[] yHist;
int iterator;
void setup() {
size(800, 400);
iterator = 0;
yHist = new float[width];
}
void draw() {
background(255);
y += yvel;
yvel += g;
if (y + radius > height) {
yvel = -yvel;
}
ellipse(x, y, radius*2, radius*2);
//graphing:
yHist[iterator] = height - y;
beginShape();
for (int i = 0; i < yHist.length; i++) {
vertex(i,
height - 0.1*yHist[i]
);
}
endShape();
iterator = (iterator + 1)%width;
}
If you run that code, you'll notice that the ball seems to bounce higher every single time. Obviously this does not happen in real life, nor should it happen even in ideal, lossless scenarios. So what happened here?
If you've ever used Euler's method for solving differential equations, you might see something about what's happening here. Really, what we are doing when we code simulations of differential equations, we are applying Euler's method. In the case of the bouncing ball, the real curve is concave down (except at the points when it bounces). Euler's method always gives an overestimate when the real solution is concave down. That means that every frame, the computer guesses a little bit too high. These errors add up, and the ball bounces higher and higher.
Similarly, with your pendulum, it's getting a little bit more energy almost every single frame. This is a general problem with using numerical solutions. They are simply inaccurate. So what do we do?
In the case of the ball, we can avoid using a numerical solution altogether, and go to an analytical solution. I won't go through how I got the solution, but here is the different section:
float h0;
float t = 0;
float pd;
void setup() {
size(400, 400);
iterator = 0;
yHist = new float[width];
noFill();
h0 = height - y;
pd = 2*sqrt(h0/g);
}
void draw() {
background(255);
y = g*sq((t-pd/2)%pd - pd/2) + height - h0;
t += 0.5;
ellipse(x, y, radius*2, radius*2);
... etc.
This is all well and good for a bouncing ball, but a double pendulum is a much more complex system. There is no fully analytical solution to the double pendulum problem. So how do we minimize error in a numerical solution?
One strategy is to take smaller steps. The smaller the steps you take, the closer you are to the real solution. You can do this by reducing g (this might feel like cheating, but think for a minute about the units you're using. g=9.81 m/s^2. How does that translate to pixels and frames?). This will also make the pendulum move slower on the screen. If you want to increase accuracy without changing the viewing pace, you can take many small steps before rendering the frame. Consider changing lines 39-46 to
int substepCount = 1000;
for (int i = 0; i < substepCount; i++) {
acc1 = (g*(sin(ang2)*cos(ang1-ang2)-mu*sin(ang1))-(l2*vel2*vel2+l1*vel1*vel1*cos(ang1-ang2))*sin(ang1-ang2))/(l1*(mu-cos(ang1-ang2)*cos(ang1-ang2)));
acc2 = (mu*g*(sin(ang1)*cos(ang1-ang2)-sin(ang2))+(mu*l1*vel1*vel1+l2*vel2*vel2*cos(ang1-ang2))*sin(ang1-ang2))/(l2*(mu-cos(ang1-ang2)*cos(ang1-ang2)));
vel1 += acc1/substepCount;
vel2 += acc2/substepCount;
ang1 += vel1/substepCount;
ang2 += vel2/substepCount;
}
This changes your one big step to 1000 smaller steps, making it much more accurate. I tested that part out and it continued for over 20000 frames multiple times with no NaN errors. It might devolve into NaN at some point, but this allows it to last much longer.
EDIT:
I also highly recommend using % TWO_PI when incrementing the angles:
ang1 = (ang1 + vel1/substepCount) % TWO_PI;
ang2 = (ang2 + vel2/substepCount) % TWO_PI;
because it makes the angle measurements MUCH more accurate in the later times.
When you don't do this, if vel1 is positive for a long time, then ang1 gets bigger and bigger. Once ang1 is greater than 1, the computer needs a bit to indicate the ones place, at the expense of an extra digit on the end. Since numbers are stored using binary, this happens again when ang1 > 2, and again when ang1 > 4, and so on.
If you keep it between -PI and PI (which is what % does in this case), you only need a bit for the sign and a bit for the ones place, and all the remaining bits can be used to measure the angle to the highest possible precision. This is actually important: if vel1/substepCount < 1/32768, and ang1 doesn't have enough bits to measure out to the 1/32768 place, then ang1 will not register the change.
To see the effects of this difference, give ang1 and ang2 really high initial values:
g = 0.0981;
ang1 = 101.1*PI;
ang2 = 101.1*PI;
If you don't use % TWO_PI, it approximates low velocities to zero, resulting in a bunch of stopping and starting.
END EDIT
If you need it to go for a ridiculously long time, so long that it isn't feasible to increase substepCount sufficiently, there is another thing you can do. This all comes about because vel increases to an extreme degree. You can constrain vel1 and vel2 so that they don't get too big.
In this case, I would recommend limiting the velocities based on conservation of energy. There is a maximum amount of mechanical energy allowed in the system based on the initial conditions. You cannot have more mechanical energy than the initial potential energy. Potential energy can be calculated based on the angles:
U(ang1, ang2) = -g*((m1+m2)*l1*cos(ang1) + m2*l2*cos(ang2))
Therefore we can determine exactly how much kinetic energy is in the system at any moment: The initial values of ang1 and ang2 give us the total mechanical energy. The current values of ang1 and ang2 give us the current potential energy. Then we can simply take the difference in order to find the current kinetic energy.
The way that pendulum motion is typically described does not lend itself to computing kinetic energy. It is possible, but I'm not going to do it here. My recommendation for constraining the velocities of the two pendulums is as follows:
Calculate the kinetic energy of the two arms separately.
Take the ratio between them
Calculate the total kinetic energy currently in the two arms.
Distribute the kinetic energy in the same proportions as you calculated in step 2. e.g. If you calculate that there is twice as much kinetic energy in the further mass as there is in the closer mass, put 1/3 of the kinetic energy in the closer mass and 2/3 in the further one.
I hope this helps, let me know if you have any questions.

Processing | How to stop the ball from changing it's color

Here's a little code that I've written to get a ball bouncing in Processing. The ball should change it's color everything it bounces off the "ground" and become slower and slower and lay at the ground at the end.
But - and that's the problem I've got - the ball doesn't stops changing it's color at the bottom - and that means it didn't stops bouncing, right?
The question is: How do I tell the ball to stop and not to change it's color anymore?
float y = 0.0;
float speed = 0;
float efficiency = 0.9;
float gravitation = 1.3;
void setup(){
size(400, 700);
//makes everything smoother
frameRate(60);
//color of the ball at the beginning
fill(255);
}
void draw() {
// declare background here to get rid of thousands of copies of the ball
background(0);
//set speed of the ball
speed = speed + gravitation;
y = y + speed;
//bouce off the edges
if (y > (height-25)){
//reverse the speed
speed = speed * (-1 * efficiency);
//change the color everytime it bounces off the ground
fill(random(255), random(255), random(255));
}
//rescue ball from the ground
if (y >= (height-25)){
y = (height-25);
}
/*
// stop ball on the ground when veloctiy is super low
if(speed < 0.1){
speed = -0.1;
}
*/
// draw the ball
stroke(0);
ellipse(200, y, 50, 50);
}
The problem is that even though you are setting the speed to -0.1 when it is small, and y to height - 25, the very next pass through the loop adds gravity to speed and then speed to y, making y larger than height - 25 (by slightly more than 1 pixel) again. This makes the ball jump up and down through an infinite loop of hops of zero height.
You could use a threshold on the reflected speed. If it is below the threshold, stop the loop.
At the top of the file, add a line like
float threshold = 0.5; //experiment with this
Then in draw(), right after the line
speed = speed * (-1 * efficiency);
add the line
if(abs(speed) < threshold) noLoop();
In this case, you can throw away the if clause which checks when the speed is super-low.

Making an object jump once with realistic gravity

I want my object (a rectangle at this point) to jump once, to lose speed on the way up. When it's almost stopped, I want it to turn back and gain speed on the way down. When it hits the ground again, it should stop. When I press the Key again it should again do the same.
The code I tried to write implement a variable float gravity = 0.75; and a variable that keeps track of the speed float speed = 10;
while the speed is greater than 1, the speed should be subtracted by the Y-coordinate of the rectangle and then the speed should get lower since I multiply it with gravity which is less than 1
else if (keyCode == UP|| key == 'w') {
while (speed >1 ) {
playerYPosition = playerYPosition-speed;
speed = speed * gravity;
}
Gravity gets higher than 1 but negative, so that the number I subtract adds up in final and the rectangle gets lower. To gain speed, the speed gets multiplied with gravity.
gravity = -1.25;
speed = speed * gravity;
playerYPosition = playerYPosition-speed;
The speed should now be around -1.2..., so it's smaller than -1 and this while(...)should work. Again it should gain speed over time till it reaches the starting speed, just negative and with that the starting point.
while (speed < -1 && speed > -10) {
playerYPosition = playerYPosition-speed;
speed = speed * gravity;
Then the gravity should again go to 0.75 and the speed to 10.
gravity = 0.75;
speed = 10;
So instead of doing that, the rectangle just constantly jumps up (I think) 10 Pixels, nothing more.
Here's the whole codeblock, to reread:
float gravity = 0.75;
float speed = 10;
else if (keyCode == UP|| key == 'w') {
while (speed >1 ) {
playerYPosition = playerYPosition-speed;
speed = speed * gravity;
}
gravity = -1.25;
speed = speed * gravity;
playerYPosition = playerYPosition-speed;
while (speed < -1 && speed > -10) {
playerYPosition = playerYPosition-speed;
speed = speed * gravity;
}
gravity = 0.75;
speed = 10;
If you use a while loop to calculate your Y position you won't be able to visualize your jump simulation since all the maths will be computed in a single frame.
To do that physic-based jump simulation you will need to have a variable representing the ground ( which is your initial Y variable ), also, once your player click, you will need to give the speed variable your full speed then each frame decrease it by the amount of your gravity while checking every frame if you haven't reached the ground yet.
Here is an example I wrote to demonstrate that, I tried keeping your variable names:
final float gravity = 0.5;
final float jmp_speed = 10; //the speed which the square is going to jump
float speed = 0; //the actual speed every frame
final float ystart = 280; //the initial Y position ( which also represent the Y of the ground )
float playerYPosition = ystart; //give the player the Y of the ground on start
void setup(){
size(300,300);
}
void keyPressed() {
//also check that the player is on ground before jumping
if( (keyCode == UP || key == 'w') && (playerYPosition == ystart) )
{
speed=-jmp_speed; //when the player press jump and he is on ground we give speed the max value
}
}
void draw(){
background(150);
rect(150,playerYPosition,10,10);
//this code will be executed every frame
//check if the player Y position won't hit the ground if we increment it by speed
if(playerYPosition+speed < ystart)
{
playerYPosition+=speed;
speed+=gravity; //increment speed by gravity ( will make speed value go from negative to positive slowly)
}
else
{
//if the player is gonna hit the ground the next frame
playerYPosition = ystart; // put back the player on ground
speed=0; // make the speed 0
}
}

Gravity simulation going haywire

I'm currently working on a Processing sketch featuring a very basic gravity simulation (based on an example given in Daniel Schiffman's book Learning Processing) but my gravity keeps behaving in a bizarre way and I'm at a loss to know what to do about it. Here's the simplest example I can come up with:
float x = 50;
float y = 50;
float speed = 2;
float gravity = 0.1;
void setup() {
size(400, 400);
}
void draw() {
background(255);
fill(175);
stroke(0);
ellipseMode(CENTER);
ellipse(x, y, 10, 10);
y = y + speed;
speed = speed + gravity;
//Dampening bounce effect when the ball hits bottom
if (y > height) {
speed = speed * -0.95;
}
}
The above is virtually identical to what's in Schiffman's book aside from a different starting speed and a different window size. It seems to work fine for the first two bounces but on the third bounce the ball becomes stuck to the bottom of the window.
I have no idea where to even begin trying to debug this. Can anyone give any pointers?
If y remains greater than height, your code just keeps flipping the speed over and over without giving the ball a chance to bounce. You want the ball to move away from the boundary whenever it is at or past the boundary.
Setting y to height in the (if y > height) block helps, but the ball never comes to 'rest' (sit on the bottom line when done).
There are two problems with Shiffman's example 5.9 which is where you probably started:
1) y can become greater than (height + speed), which makes it seem to go thud on the ground or bounce wildly
-- fixed with the suggestion
2) The algorithm to update y does things in the wrong order, so the ball never comes to rest
Correct order is:
if it is time to 'bounce
negate speed including dampening (* 0.95)
set y to location of the 'ground'
Add gravity to speed
Add speed to y
This way, when it is time to bounce (y is > height), and speed is positive (going downward):
speed is set to negative (times dampening)
y is set to height
speed is set less negative by the gravity factor
y is made less positive by adding the (still negative) speed
In my example, I didn't want the object to disappear so I use a variable named 'limit' to be the lowest (most positive y) location for the center of the object.
float x = 50;
float y = 50;
float speed = 2;
float gravity = 0.1;
void setup() {
size(400, 400);
y = height - 20;
}
void draw() {
background(255);
fill(175);
stroke(0);
ellipseMode(CENTER);
ellipse(x, y, 10, 10);
float limit = height - (speed + 5);
//Dampening bounce effect when the ball hits bottom
if (y > limit) {
speed = speed * -0.95;
y = limit;
}
speed = speed + gravity;
y = y + speed;
}

How can I make this Processing.org sketch more efficient?

I have a simple sketch (in Processing), basically a bunch of dots wander around, if they come into contact with each other they fight (each has a strength value, increased each time they win, if it's equal the winner is randomly chosen)
It works well with about 5000 12-pixel "zombies" (there's a slight slowdown for a half a second, while the zombies initially collide with each other), the problem is when the zombies are made smaller, they don't collide with each other as quick, and the slowdown can last much longer..
The code is really simple - basically each zombie is a class, which has an X/Y coordinate. Each frame all the zombies are nudged one pixel, randomly turning lurching degrees (or not). I think the biggest cause of slowness is the collision detection - each zombie checks every other one (so zombie 1 checks 2-5000, zombie 2 checks 1,3-5000 etc..)
I'd like to keep everything simple, and "plain Processing" (not using external libraries, which might be more efficient and easy, but I don't find it very useful for learning)
int numZombies = 5000;
Zombie[] zombies = new Zombie[numZombies];
void setup(){
size(512, 512);
noStroke();
for(int i = 0; i < numZombies; i++){
zombies[i] = new Zombie(i, random(width), random(height), random(360), zombies);
}
}
void draw(){
background(0);
for(int i = 0; i < numZombies; i++){
zombies[i].move();
zombies[i].display();
}
}
class Zombie{
int id; // the index of this zombie
float x, y; // current location
float angle; // angle of zombies movement
float lurching = 10; // Amount angle can change
float strength = 2;
boolean dead = false; // true means zombie is dead
float diameter = 12; // How big the zombie is
float velocity = 1.0; // How fast zombie moves
Zombie[] others; // Stores the other zombies
Zombie(int inid, float xin, float yin, float inangle, Zombie[] oin){
id = inid;
x = xin;
y = yin;
angle = inangle;
others = oin;
}
void move(){
if(dead) return;
float vx = velocity * sin(radians(180-angle));
float vy = velocity * cos(radians(180-angle));
x = x + vx;
y = y + vy;
if(x + vx < 0 || x + vx > width || y + vy < 0 || y + vy > height){
// Collided with wall
angle = angle + 180;
}
float adecide = random(3);
if(adecide < 1){
// Move left
angle=angle - lurching;
}
else if(adecide > 1 && adecide < 2){
// Don't move x
}
else if(adecide > 2){
// Move right
angle = angle + lurching;
}
checkFights();
}
void checkFights(){
for (int i=0; i < numZombies; i++) {
if (i == id || dead || others[i].dead){
continue;
}
float dx = others[i].x - x;
float dy = others[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
if (distance < diameter){
fight(i);
}
}
}
void fight(int oid){
Zombie o = others[oid];
//println("Zombie " + id + "(s: "+ strength +") fighting " + oid + "(s: "+ o.strength +")");
if(strength < o.strength){
kill();
o.strength++;
}
else if (strength == o.strength){
if(random(1) > 0.5){
kill();
o.strength++;
}
else{
o.kill();
strength++;
}
}
}
void kill(){
dead = true;
}
void display(){
if(dead) return;
ellipse(x, y, diameter, diameter);
}
}
You got yourself O(n^2) complexity, and that's killing your algorithm. It's correct that each zombie that moves has to check with all the others if they collided which brings you to quadratic complexity.
One direction might be to create a matrix representing your screen, and instead of iterating over all the other zombies, simply update the current zombie's location on the matrix, and check there if another zombie is already occupying that same cell.
Like 1800 INFORMATION says, somehow you need to reduce the number of comparisons.
Splitting the playing area into zones is a good idea. I would imagine the time it takes to compare current location against zone boundaries and add/remove zombies from the appropriate collections is worth it. Assuming they generally will go in straight lines, they shouldn't be changing zones too frequently.
We have the problem though of possible collisions between zones. To piggyback on the idea, you could divide the screen into 4 zones then 9 zones again. Think a tic-tac-toe board overlaid on a cross. This is a bad drawing, but:
| ! |
| ! |
----+--!-+----
| ! |
====|==x=|====
----+--!-+----
| ! |
| ! |
This way each zombie is in two zones at once and every border in one scheme is covered by another zone. You wouldn't even have to check all the same zombies again because either we'd be dead or they would. So the only double-processing is a single others[i].dead check.
Another thing I can see quickly is you still loop through the rest of the elements even though you're dead:
if (i == id || dead || others[i].dead){
continue;
}
It might not save a lot of processing, but it can certainly cut some instructions if you:
if (dead) return;
instead.
Also as a side note, do you want to be checking the diameter or the radius against the distance?
Your basic collision detection algorithm has O(n^2) complexity.
You need some approach which will reduce the number of comparisons.
One approach already mentioned, is to divide the playing field into zones/regions, and
only check for collision when a zombie is in the same zone/region. This is an attempt
to sort the entities topologically (by distance). What you want is to separate these
zombies not simply by geography, but to sort them so that they are only compared when
they are 'close' to one another. And you want to ignore empty regions.
Consider a tree structure to your regions. When a region has more than some number N of zombies, you could split the region smaller, until the region radius approaches your collision distance. Use a map to lookup region, and check all zombies in a given region (and any 'close enough' region).
You probably want N to be <= log(n)...
Maybe you should split the playing field up into zones and only check for collisions between zombies that are in the same zone. You need to reduce the number of comparisons.
It reminds me of this thread: No idea what could be the problem!!. And collision detection help where I point to Wikipedia's Collision detection article.
Quadtrees seem to be often used for 2D partitioning.

Resources