Collision with Spring Dynamics

Hi

I’m trying to enable bouncing balls effect with spring dynamics.
I’ve tweaked the chain example a bit a here’s the code I’m working on:

Spring2D s1, s2;

float gravity = 9.0;
float mass = 5;

void setup() {
  size(800, 600);
  fill(255, 126);
  // Inputs: x, y, mass, gravity
  s1 = new Spring2D(0.0, width/2, mass, gravity);
  s2 = new Spring2D(0.0, width/2, mass, gravity);
}

void draw() {
  background(0);
  
  s1.update(mouseX, mouseY);
  s1.display(mouseX, mouseY);
  s2.update(s1.x, s1.y);
  s2.display(s1.x, s1.y);
  
  
  // the collider rectangle
  
  stroke(255);
  strokeWeight(2);
  rect(50, 50, 700, 500);
  
  
  
}

class Spring2D {
  float vx, vy; // The x- and y-axis velocities
  float x, y; // The x- and y-coordinates
  float gravity;
  float mass;
  float radius = 30;
  float stiffness = 0.2;
  float damping = 0.9;
  
  Spring2D(float xpos, float ypos, float m, float g) {
    x = xpos;
    y = ypos;
    mass = m;
    gravity = g;
  }
  
  void update(float targetX, float targetY) {
    float forceX = (targetX - x) * stiffness;
    float ax = forceX / mass;
    vx = damping * (vx + ax);
    x += vx;
    float forceY = (targetY - y) * stiffness;
    forceY += gravity;
    float ay = forceY / mass;
    vy = damping * (vy + ay);
    y += vy;
    
    
    if (x <= 50) {
    mouseX = 50 + int(radius);
    } 
  }
  
  void display(float nx, float ny) {
    noStroke();
    ellipse(x, y, radius*2, radius*2);
    stroke(255);
    line(x, y, nx, ny);
  }
}

The rectangle line is the border and I don’t want the balls to cross hat but it does even if I tell it not to. See:

    if (x <= 50) {
    mouseX = 50 + int(radius);

I think it’s because dynamics still apply.

Then I tried:

    if (x <= 50) {
    s1.x = 50 + int(radius);
    s2.x = 50 + int(radius);
    }

With that, I’m getting a weird flashing balls.

How can I achieve bounce-back effect relative to damping, stiffness, mass?

I will try to follow want you want to do

s1. Update:

MouseX and mouseY are used as target for your object. You calculate the difference to the current x, incl. a reducing stiffness factor.
Then you normalize that with the mass and add it to the velocity (again with a dampening factor).
The same you do for y but you add gravity. (First problem you add gravity with every update)
This way your ball will move in direction of the mouse cursor.

After that you check if x is smaller 50. If yes, you change mouseX to 50+radius?

Why do you change mouseX, that messes up your collection of mouse coordinates?
Then your mouseX is not the coordinate of the mouse cursor anymore.

What you want is that if x is smaller 50, that your velocity becomes negative to bounce in the opposite direction.
The bounce should be dampened, that means just turning the sign on the velocity, gives you a 100% elastic bounce, so you have to reduce the velocity at the same time by a factor.

You might also have to check how fast your balls become. If they travel faster then the radius in one frame, they might be able to get beyond the barrier.

You don’t need s2 using s1 coordinates. The update for s1 will automatically happen with every frame. So just displaying s1 you should see the movement of the ball.

1 Like

Thanks @Divitiacus

The problem is that there’s no velocity value that embodies every factor. Do you think getting ‘damping’ negative would solve it?

And I don’t have the slightest idea as to how to calculate how fast the ball hits the barrier.
A bit of code would be really helpful. I feel stuck.

Thank again.

Alright, first for your ball you need three variables, coordinates, velocity and acceleration.
The easiest is to define in your class a PVector, that is a variable, which automatically has an x and y component. Therefore, you can use a vector for each variable and you are covered.
Adding your velocity vector to the location vector every round will give you movement and add the acceleration vector to velocity gives you your velocity change.
How to use that is well described in the book Nature of Code:

Below is an example for the class for particles in a 3D box
Here is the collision check for a particle in a box (three dimensions). Boxsize/2 is the limit (your 50), so you can apply that for your problem.

This only covers the collision check. You then need the update, you already created for normal movement.
The collisioncheck does the following: It checks if the next step for the ball (location+size+velocity) is larger then the coordinate of the wall, if yes, it calculates the bounce back location by calculation the distance to the wall from the current location and then using that to calculate, how far the ball bounces back from the wall.
Furthermore, the velocity to x is reversed to the negative value, so that the ball continues to move away from the x wall (the same for the other coordinates). The only thing here you have to change is, that velocity.x *= -dampening; With dampening you can then adjust how much the ball bouncing is dampened (1 no dampening, 0 no bounce, stays on wall).

Hope that helps.

  PVector location;
  PVector velocity;
  int size;
  boolean ccpos = false; //marker for collision check positive

  Particle(PVector location_, PVector velocity_, int size_){
    location = location_;
    velocity = velocity_;
    size = size_;
    }
 
  void display(){
    pushMatrix();
    translate(location.x,location.y,location.z);
    noStroke();
    fill(255, 248, 38);
    sphere(size);
    popMatrix();
  } 
  
  void update(){ 
    // the if check avoids that a second step is made when the collision check was 
    // positive and the new location was already defined there
    // new location is only calculated when collision check was not positive, if it was
    // ccpos is only set back to false for the next frame
    if (ccpos == false) {
    location.add(velocity);
    } else {
      ccpos = false;
    }
  }
  
  void collisioncheck(){
    //wallcheck
    //if velocity is larger then distance to wall, particle might leave wall, therefore
    //velocity included in check and new coordinates are calculated based on the assumption
    //that particle fully moves to wall and then remaining distance back
    //equation to calculate the new coordinate is coord = 2*wallcoord - 2*size - velocity - coord
    //because wall coord is boxsize/2, 2 is eliminated here
    //for negative coordinates sign has to change for boxsize and size, because both have no negative values

    if (location.x+size+velocity.x >= boxsize/2) {
      location.x = boxsize - velocity.x - location.x - 2*size;
      velocity.x *= -1;
      ccpos = true;
    }
    if (location.x-size+velocity.x <= -boxsize/2) {
      location.x = -boxsize - velocity.x - location.x + 2*size;
      velocity.x *= -1;
      ccpos = true;
    }
    if (location.y+size+velocity.y >= boxsize/2) {
      location.y = boxsize - velocity.y - location.y - 2*size;
      velocity.y *= -1;
      ccpos = true;
    }
    if (location.y-size+velocity.y <= -boxsize/2) {
      location.y = -boxsize - velocity.y - location.y + 2*size;
      velocity.y *= -1;
      ccpos = true;
    }
    if (location.z+size+velocity.z >= boxsize/2) {
      location.z = boxsize - velocity.z - location.z - 2*size;
      velocity.z *= -1;
      ccpos = true;
    }
    if (location.z-size+velocity.z <= -boxsize/2) {
      location.z = -boxsize - velocity.z - location.z + 2*size;
      velocity.z *= -1;
      ccpos = true;
    }
}
}
2 Likes

Thank you so much @Divitiacus

I really thought it’d take a few lines of code to achieve that ‘simple’ bounce. I’m hesitant to apply the workaround you suggest for this because the original ‘chain’ simulation code I’m working on is almost production ready and complex enough. It seems so many things must be changed. But I’m still open to simpler suggestions.

Now I’m having a problem with implementing the barriers. Maybe you can help me on that?

Please check the ‘COLLIDER EDGE’ part in void update():

Spring2D s1, s2;

float gravity = 9.0;
float mass = 5;
int barrier = 50;

void setup() {
  size(800, 600);
  fill(255, 126);
  // Inputs: x, y, mass, gravity
  s1 = new Spring2D(0.0, width/2, mass, gravity);
  s2 = new Spring2D(0.0, width/2, mass, gravity);
}

void draw() {
  background(0);
  
  s1.update(mouseX, mouseY);
  s1.display(mouseX, mouseY);
  s2.update(s1.x, s1.y);
  s2.display(s1.x, s1.y);
  
  
  // the collider rectangle
  
  stroke(255);
  strokeWeight(2);
  rect(50, 50, 700, 500);
  
  
  
}

class Spring2D {
  float vx, vy; // The x- and y-axis velocities
  float x, y; // The x- and y-coordinates
  float gravity;
  float mass;
  float radius = 30;
  float stiffness = 0.2;
  float damping = 0.9;
  
  Spring2D(float xpos, float ypos, float m, float g) {
    x = xpos;
    y = ypos;
    mass = m;
    gravity = g;
  }
  
  void update(float targetX, float targetY) {
    float forceX = (targetX - x) * stiffness;
    float ax = forceX / mass;
    vx = damping * (vx + ax);
    x += vx;
    float forceY = (targetY - y) * stiffness;
    forceY += gravity;
    float ay = forceY / mass;
    vy = damping * (vy + ay);
    y += vy;
    
    
 // COLLIDER EDGE       
    if (x < barrier)  {
     x = barrier;  
    } 
    else if (x > width - barrier) {
    x = width - barrier;
    } 
    else if (y < barrier) {
      y = barrier;
    } 
    else if (y > height - barrier) {
      y = height - barrier;
    }
// COLLIDER EDGE        
        
  }
  
  void display(float nx, float ny) {
    noStroke();
    ellipse(x, y, radius*2, radius*2);
    stroke(255);
    line(x, y, nx, ny);
  }
}

When you go to corners it goes out of (escapes from) the boundries though I strictly tell x and y not to go lower than barrier (50px). What am I missing?

Thanks.

At the moment you only check for the barrier on the y axis, when your x did not pass the barrier, because it is in the else if statement. You have to do y barrier check separately from x, with a separate if statement.

Currently your balls can overlap over the barrier, because you don’t compensate for the radius.
You should check for x < barrier+radius or x > width-barrier-radius. Same for y.
The new coordinates you set, have to be adjusted accordingly as well. New x= barrier+radius etc.

Finally, if you want that the balls bounce back from the wall, add a vx *=-1 to your x wall check and the same with vy for the check of the y coordinates.

Oh @Divitiacus you’re simply adorable! Thanks a million. I’m adding the working code below.

I’ve got one last question if you don’t mind. I’m also trying to achieve a sort of collision detection effect relative to the brightness of background, darker areas acting as colliders. The problem is that it’s not as easy as the barriers, which is about one-dimensional check. Is there any working logic better than calculating where the ball comes from and where it goes to compensate for the radius? If you check my code, you’ll see that it takes the point x and y to stop. I’d like to get the entire circumference as the basis of collision. What should I try?

Spring2D s1;

float gravity = 4;
float mass = 3;
int barrier = 80;

void setup() {

  size(800, 600, P2D);
  fill(255, 126);
  // Inputs: x, y, mass, gravity
  s1 = new Spring2D(0.0, width/2, mass, gravity);
}

void draw() {
  background(0);


  // BARRIER
  stroke(255);
  strokeWeight(4);
  fill(245);
  rect(50, 50, 700, 500);
  // BARRIER


  // COLLIDER OBJECT, RECTANGLE
  fill(17);
  noStroke();
  rect(440, 220, 150, 150);


  //COLLIDER OBJECT, CIRCLE
  fill(17);
  noStroke();
  ellipse(220, 250, 150, 150);


  s1.update(mouseX, mouseY);
  s1.display(mouseX, mouseY);



  // the collider rectangle
}

class Spring2D {
  float vx, vy; // The x- and y-axis velocities
  float x, y; // The x- and y-coordinates
  float gravity;
  float mass;
  float radius = 30;
  float stiffness = 0.1;
  float damping = 0.88;

  Spring2D(float xpos, float ypos, float m, float g) {
    x = xpos;
    y = ypos;
    mass = m;
    gravity = g;
  }

  void update(float targetX, float targetY) {
    float forceX = (targetX - x) * stiffness;
    float ax = forceX / mass;
    vx = damping * (vx + ax);
    x += vx;
    float forceY = (targetY - y) * stiffness;
    forceY += gravity;
    float ay = forceY / mass;
    vy = damping * (vy + ay);
    y += vy;


    // COLLIDER EDGE       
    if (x < barrier) {
      x = barrier;  
      vx *=-1;
    } else if (x > width - barrier) {
      x = width - barrier;
      vx *=-1;
    } 
    if (y < barrier) {
      y = barrier;
      vy *=-1;
    } else if (y > height - barrier) {
      y = height - barrier;
      vy *=-1;
    }
    // COLLIDER EDGE        


    color c = get(int(x), int(y));

    float shadeOfGrey = brightness(c);
    noStroke();

    // tolerance of the dark areas, the grey parts lower than 50 act as colliders
    if (shadeOfGrey < 50) {

      x -= (vx);
      y -= (vy);
      vy *=-1;
      vx*= -1;
    }
  }

  void display(float nx, float ny) {
    noStroke();
    fill(255, 0, 0);
    ellipse(x, y, radius*2, radius*2);
    stroke(255);
    line(x, y, nx, ny);
  }
}

What you need to do is to calculate the distance of the collider to your ball and then check, if it is smaller than the size of the collider and your ball together.

For the rectangle you can check x and y separately with abs(xc-xb) xc collider x and xb the ball x. abs calculates the magnitude of the number.
Then you need to create a if condition, which checks if the result for both, x and y, is smaller then half the rect size + ball radius. If yes, do -vx and -vy.

For the spherical collider it is easiest to use dist (xc,yc,xb,yb). That calculates the distance, which has to be smaller then radius collider+radius ball.

That will give you a bounce back to the direction the ball is coming from. That is physically not totally correct, but I think the best you can do without using vectors.

If you want to create several colliders, create an array with all their coordinates and then a for loop to circle to all of them to check their distances to the ball individually.

1 Like