4

I am trying to write a simple physics simulation where balls with varying radii and masses bounce around in a perfectly elastic and frictionless environment. I wrote my own code following this resource: http://www.vobarian.com/collisions/2dcollisions2.pdf and I also tested the code from here: Ball to Ball Collision - Detection and Handling

QUESTION EDITED

With the help of Rick Goldstein and Ralph, I have gotten my code to work (there was a typo..). Thanks so much for you help. However I am still confused as to why the other algorithm isn't working for me. The balls bounce off in the correct directions, but the total energy of the system is never conserved. The velocities get faster and faster until the balls just start blinking in static positions on the screen. I actually want to use this code in my program, because it is a lot more concise than the one I wrote.

Here is the functional algorithm that I wrote (although I did take the first bit from that other source). Its in a Bubble class:

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / b.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    //get the unit normal and unit tanget vectors
    Vector2 uN = b.position.subtract(this.position).normalize();
    Vector2 uT = new Vector2(-uN.Y, uN.X);

    //project ball 1 & 2 's velocities onto the collision axis
    float v1n = uN.dot(this.velocity);
    float v1t = uT.dot(this.velocity);
    float v2n = uN.dot(b.velocity);
    float v2t = uT.dot(b.velocity);

    //calculate the post collision normal velocities (tangent velocities don't change)
    float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass+b.mass);
    float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

    //convert scalar velocities to vectors
    Vector2 postV1N = uN.multiply(v1nPost);
    Vector2 postV1T = uT.multiply(v1t);
    Vector2 postV2N = uN.multiply(v2nPost);
    Vector2 postV2T = uT.multiply(v2t);

    //change the balls velocities
    this.velocity = postV1N.add(postV1T);
    b.velocity = postV2N.add(postV2T);
}

And here is the one that doesn't work

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / b.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    // impact speed
    Vector2 v = (this.velocity.subtract(b.velocity));
    float vn = v.dot(mtd.normalize());

    // sphere intersecting but moving away from each other already
    if (vn > 0.0f) return;

    // collision impulse (1f is the coefficient of restitution)
    float i = (-(1.0f + 1f) * vn) / (im1 + im2);
    Vector2 impulse = mtd.multiply(i);

    // change in momentum
    this.velocity = this.velocity.add(impulse.multiply(im1));
    b.velocity = b.velocity.subtract(impulse.multiply(im2));
}

Let me know if you find anything. Thanks

Community
  • 1
  • 1
Cbas
  • 6,003
  • 11
  • 56
  • 87

3 Answers3

6

Is there a typo in the line that sets v1nPost? Looks like the denominator should be this.mass + b.mass, not this.mass * b.mass.

Also, because you're computing a collision between this and b, are you checking to make sure you're not also doing the same collision between b and this, thus doubling the delta applied to each participating bubble in the collision?

Rick Goldstein
  • 299
  • 1
  • 6
  • Ya it was the typo... I must have been in front of my computer for too long, I checked the math over many times. Thanks. Check out my edit though, I really want to get the other algorithm working and its definitely not the same problem – Cbas Feb 16 '11 at 02:51
  • As for your second point, I don't check to see if I have already resolved the collision in my loop through the bubbles, but I don't think it matters since the first thing I do in this resolveCollision method is pull them apart. When the loop does find the other ball in the collision, it will no longer be colliding and won't call this method again – Cbas Feb 16 '11 at 02:56
  • 1
    When you compute `mtd.normalize()`, does `mtd` stay normalized? If not, I think you need to change the calculation of `impulse` to `mtd.normalize().multiply(i)`. Beyond that, you may be looking at accumulated rounding errors, especially from the translation into the reference frame of `b` (the 'impact speed' calculation). – Rick Goldstein Feb 16 '11 at 21:06
  • Thats it! Works great now. I honestly didn't understand all the math behind this one since I just pulled it from another source, so thank you so much for helping me sort it out. I'll be sure to write a comment on that other thread informing them that theres a mistake in the code – Cbas Feb 17 '11 at 01:25
  • Glad that helped. I was a physicist in a former life, so I've done collision problems once or twice. Cheers! – Rick Goldstein Feb 17 '11 at 03:09
3

I do a first guess: getMass() return an integer(or int) (and not a float or double)?

If this is true, than you problem is that 1 / getMass() will result in an integer value (and can be only 1 or most time 0)). To fix this replace 1 by 1.0 or 1.0f

Because the general rule is simple: If you have a math operation (+,-,*,/) the resulting type will be integer if none of the both operants is a floating point data structure (double or float)

Anyway: there could be a second problem, may your calcualtion is not precise enougth. Then you should use double instead of float.

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • Thats a very good point. getMass() actually returns a byte, and in my code so far, all the bubbles have a mass of 1. If the masses differed, I see that I would have a problem. I'll change it, Thanks a lot. – Cbas Feb 16 '11 at 03:00
1

There is a part that looks strange:

The two calculations:

float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass*b.mass);
float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

are symmetric, except the last operation, in the first it is * in the second it is +

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • That was it.. Thanks a lot. Check out my edit though, I'm now trying to get the other algorithm working – Cbas Feb 16 '11 at 02:57