# Two-Dimensional Collision With Two Moving Objects

Tags:
1. Jul 31, 2015

### Vold

1. The problem statement, all variables and given/known data

Two moving objects (circle shaped) with a known mass, move in a 2D plane with a known constant direction and speed, at certain point, objects collide with eachother (elastic). At that point, the coordinates of the center each object is known, their radius is also known, their Vx and Vy are known (everything whats needed is known, almost). Determine the final speed vector for each object

2. Relevant equations

I found this equation:

What exactly are the theta and phi angles and how are they calculated on a cartesian plane.
Are they calculated in radians or degrees for that equation (I guess that they are in radians, due to the final 1/2 pi).

3. The attempt at a solution

I have some troubles determining the angles (theta and phi), because I am not sure whether or not I have to add them certain values (180, 180, 360) depending on the different quadrants( +x+y, -x+y, -x-y, +x-y).

Thank you very much!

2. Jul 31, 2015

### haruspex

A physics equation has no value unless the variables it uses are defined. Wherever you found those equations, you should also find those definitions. Maybe you don't understand the definitions given, but if so you need to quote those definitions in your post so that others can interpret them, but it's a bit tough trying to guess the definitions from these equations alone.

Better still, ignore those equations you found, make up your own variable names, and people on this forum can help you develop your own equations from the basic laws that apply. What are those laws?

3. Jul 31, 2015

### Vold

I've got those equations on this website, also on wikipedia. :P

Anyways, here is what I am trying to do, I hope you can help me with it.

I'm trying to program with Java, a small 2D game (just for fun and learning) in which I use circles as objects with certain radius which move with a speed given on pixels and updates the position on the X/Y Axis 60 times per second. The X/Y Axis is kinda awkward, because the 0,0 coordenates of the window on which the textures are rendered is at the top left of the window and the Y Axis is inverted (down increases and up decreases). With that in mind, I have to determine the point of impact of two objects based on their position (x,y), velocity (velx, vely : for example with :velx=2, vely=2, the rendered circle moves down to the right side of the window), radius and mass.

The collision is calculated based on the radius of the objects and their position [ ( ( x2-x1)^2 + (y2-y1)^2 )^1/2 <= r2+r1 ] (x1,y1/x2,y2 the position of the objects based on coordenates, and r1,r2 their radius). It works like a charm, so when they are touching I run the script that makes them change their velocity; which I have been trying to accomplish with no success. With the equation of the opening post I managed to have some good collisions, regardless, I have problems sometimes where the objects do not move properly.

So, I have all the information needed to do the math, but the main problem I have is the inverted Y Axis, which messes up with the angles and I have not managed to make it right. I hope you guys can help me out. Thanks in advance!

4. Jul 31, 2015

### haruspex

Based on the diagram at the link, the two theta angles are the angles to the x axis made by the trajectories of the approaching discs. Phi is the angle to the x axis made by the line through the two centres when the discs touch.
The author has been inconsistent, showing theta2 as measured from the -ve x direction but the other two from the +ve x. Looking at the equations, theta2 is really measured from the +ve x direction, so the diagram is wrong. The equations' theta2 is pi plus the angle shown. (Getting this wrong will affect the signs of the trig functions.)
All three are measured clockwise from the reference, whereas the Cartesian standard is to measure anticlockwise. However, that makes sense because down is +ve Y in the computer's representation.
Getting the trig signs right can be quite a challenge.
E.g. for finding theta1, you use arctan(vy/vx), but that only gives an answer in a range of pi (-pi/2 to +pi/2 I would guess). For the equations, you need to know the angle in a full 2pi range. To determine that, you have to look at the sign of vy and/or vx. If vx is negative the object is heading left, so the angle must be in the range pi/2 to 3pi/2. To correct, just add pi to the arctan result.
You also need to protect against zero-divide in calculating vy/vx.

Another thought....
Sounds as though your time intervals are small enough that there are no funny effects where objects overlap etc. But if that is a problem, you need to detect that the collision occurred somewhere in the last time interval, compute exactly when that was, execute the bounce logic on that basis, then position the objects based on how they would have travelled since.
Of course, this can get quite hairy with multiple collisions by the same object in one time interval.

5. Aug 2, 2015

### Vold

Thanks a lot for your help. I have corrected the equation based on what you have suggested, I just have to find the correct angles to make the objects bounce.

I have a few questions:

1. Do I have to use the same phi for both objects?(pretty sure I have to).

2.
theta2 = (PI + "theta2")

In regards to the last thought, I have been reading about how to calculate the exact position of impact to correct the location of the objects so that they do not overlap. In the meanwhile, to test the correct final velocity of each object, I make them collide just once with a boolean field.

Another issue that I have been thinking on is that I calculate the distance between each object based on their position (x,y) to calculate the distance between their centers and comparing it to their radius, with a pythagorean theorem, but it only work if the objects are within the frame (+x,+y).

Edit:

I ran 4 collisions and here you can see the results:

Code (Text):

Initial position, direction and speed
objects have same radius and same mass (2*PI*r, just used the area of the circle to give them mass).
vx, vy: pixels per 0,016666 seconds (game updates 60 times per second).

Angle of collision
Phi 44.55387947558248

gO1
x:610.0
y:307.0
Vx:-1.0
Vy:-1.0
theta, deg:45.0

gO2
x:564.7000000000047
y:262.39999999999765
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:610.0
y:307.0
Vx:0.1347928708643486
Vy:0.0780045497865444
theta, deg:30.057939577479935

gO2
x:564.7000000000047
y:262.39999999999765
Vx:0.6618245460937124
Vy:0.753142930591247
theta, deg:48.6926021887038

Result: awkward collision

Initial position, direction and speed

Angle of collision
Phi-38.54716002467078

gO1
x:518.0
y:308.0
Vx:1.0
Vy:-1.0
theta, deg:-45.0

gO2
x:567.7000000000054
y:268.3999999999973
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:518.0
y:308.0
Vx:0.05737619804002281
Vy:-0.18661970335408506
theta, deg:-72.9099627395368

gO2
x:567.7000000000054
y:268.3999999999973
Vx:-0.6457889329971941
Vy:-0.45208696110824664
theta, deg:34.994086837063115

Result: Collision seemed correct

Initial position, direction and speed

Angle of collision
Phi:59.37821096161859

gO1
x:549.0
y:241.0
Vx:1.0
Vy:1.0
theta, deg:45.0

gO2
x:581.2000000000085
y:295.39999999999577
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:549.0
y:241.0
Vx:0.4040808389247806
Vy:-0.09282509782172968
theta, deg:-12.937479313695539

gO2
x:581.2000000000085
y:295.39999999999577
Vx:0.49575577862969233
Vy:0.8686060359455879
theta, deg:60.28449181093118

Result: awkward collision

Initial position, direction and speed

Angle of collision
Phi-55.11124190812558

M1
x:619.0
y:246.0
Vx:-1.0
Vy:1.0
theta, deg:-45.0

M2
x:582.6000000000088
y:298.1999999999956
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

M1
x:619.0
y:246.0
Vx:0.318051489820771
Vy:0.059985539221356285
theta, deg:10.680706869495856

M2
x:582.6000000000088
y:298.1999999999956
Vx:-0.4108658742107923
Vy:-0.7079128926404634
theta, deg:59.869530325480376

Result: awkward collision

Last edited: Aug 2, 2015
6. Aug 2, 2015

### haruspex

Just to be clear, the equations shown at the link are correct. The error is merely in the way theta2 is drawn in the diagram.
Yes.
Because Vx<0 you need to add pi. And as I mentioned, you need to check for Vx = 0 before calculating the ratio for arctan's argument.
A possible approach is to drive the iterations by events, not time intervals.
From current position, inspect each pair of objects to see when they will collide (if they will). Take the shortest time.
While the remaining time to the next event is greater than your chosen refresh interval, iterate sleeping and redrawing.
When the time to the next event is less than the refresh interval, sleep for only that time, etc.
Not sure what you are saying here.
What is your policy for objects which move outside the displayable region? In principle you need to keep track of them because a fast moving object may exit the scene only to collide with a bunch of slow moving ones that already left. The result may be that the fast moving one ricochets back into view.
Or are you saying the equations go wrong when x or y is negative?

Feel free to post your code snippets.

7. Aug 2, 2015

### Vold

Code (Java):

//Declaration of the final velocity values of

//the loop.
for(int i = 0; i < gameObjects.size(); i++){
for (int j = i+1; j < gameObjects.size(); j++){

//Assigns the object located in i position to gO1 and j position to gO2
GameObject gO1 = gameObjects.get(i);
GameObject gO2 = gameObjects.get(j);

//checks if the objects intersect using a method of gameObject's class.
if(gO1.intersects(gO2)){
//checks if the objects intersecting eachother have different faction,
//added this in case that I want a specific object not to be checked for collisions.
if (gO1.getFaction() != gO2.getFaction()){

//debug info.

System.out.println("");
System.out.println("Initial position, direction and speed");
System.out.println("");
System.out.println("Angle of collision");
System.out.println("Phi"+Math.toDegrees(gO1.phi(gO2)));
System.out.println("");
System.out.println("M1");
System.out.println("x:"+gO1.getXr());
System.out.println("y:"+gO1.getYr());
System.out.println("Vx:"+gO1.getVelX());
System.out.println("Vy:"+gO1.getVelY());
System.out.println("theta, deg:"+Math.toDegrees(gO1.theta()));
System.out.println("");
System.out.println("M2");
System.out.println("x:"+gO2.getXr());
System.out.println("y:"+gO2.getYr());
System.out.println("Vx:"+gO2.getVelX());
System.out.println("Vy:"+gO2.getVelY());
System.out.println("theta, deg:"+Math.toDegrees(gO2.theta()));

//calculates the final Vx of gO1.
gO1Vxf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) ) * Math.cos(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vy of gO1.
gO1Vyf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.sin(gO2.theta() - gO1.phi(gO2))) ) * Math.sin(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vx of gO2.
gO2Vxf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) ) * Math.cos(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vy of gO2.
gO2Vyf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.sin(gO1.theta() - gO1.phi(gO2))) ) * Math.sin(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );

//sets the final velocity of each object.
gO1.setVelX(gO1Vxf);
gO1.setVelY(gO1Vyf);
gO2.setVelX(gO2Vxf);
gO2.setVelY(gO2Vyf);

//those objects can no longer collide (temporary "solution", to prevent objects getting stuck)
gO2.setFaction(gO1.getFaction());

//More debug info.

System.out.println("");
System.out.println("Final position(same), direction and speed");
System.out.println("");
System.out.println("M1");
System.out.println("x:"+gO1.getXr());
System.out.println("y:"+gO1.getYr());
System.out.println("Vx:"+gO1.getVelX());
System.out.println("Vy:"+gO1.getVelY());
System.out.println("theta, deg:"+Math.toDegrees(gO1.theta()));
System.out.println("");
System.out.println("M2");
System.out.println("x:"+gO2.getXr());
System.out.println("y:"+gO2.getYr());
System.out.println("Vx:"+gO2.getVelX());
System.out.println("Vy:"+gO2.getVelY());
System.out.println("theta, deg:"+Math.toDegrees(gO2.theta()));

}

}

}

}

}
Code (Java):

/**
*
* @param m
* @return whether the objects intersect or not.
* It calculates the distance of the objects using phythagoras theorem: a*a + b*b = c*c,
* checking a*a + b*b with the sum of the radious of the objects.
*/

public boolean intersects(GameObject m){

if( Math.pow(this.getXr() - m.getXr(), 2 ) + Math.pow(this.getYr() - m.getYr() , 2 ) <= Math.pow(this.getRadius() + m.getRadius(), 2) )

return true;
else
return false;

}

/**
*
* @return  theta: the angle of the direction of the object with the X axis.
* currently the conditions are only useful to prevent Y/0 division (pretty sure it
* does not use the best solution to it - adding 0.001). It calculates the angle with
* Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
* it does not cover the full 2 PI range. But, what are those corrections? Im clueless.
*/

public double theta(){

if(this.getVelX() == 0 && this.getVelY() == 0)
this.setVelX(0.001);

if(this.getVelY() > 0 && this.getVelX() <= 0){

return Math.PI + Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() >= 0 && this.getVelX() > 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() < 0 && this.getVelX() >= 0){

return 2*Math.PI + Math.atan(this.getVelY()  / this.getVelX() );

}else //if(this.getVelY() <= 0 && this.getVelX() < 0)

return Math.PI + Math.atan(this.getVelY()  / this.getVelX()) ;

}
/**
*
* @param m
* @return phi: the angle of the collision of the objects with the X axis.
* currently the conditions are only useful to prevent Y/0 division (pretty sure it
* does not use the best solution to it - adding 0.001). It calculates the angle with
* Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
* it does not cover the full 2 PI range. But, what are those corrections? Im clueless.
* Note: the position of the objects is based on x+radius, to check where is the center,
* because x,y point is located at the upper left of the object.
*/

public double phi(GameObject m){

if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() <= 0){

return Math.PI + Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() >= 0 && this.getXr() - m.getXr() > 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() >= 0){

return  Math.PI + Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
//ok
}else //if(this.getYr() - m.getYr() <= 0 && this.getXr() - m.getXr() < 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}

public double getXr() {
}

public double getYr() {
}
I wouldn't be surprised if the errors are due to typos, I just fixed several in other part of the code. :p

I think that I am getting close to the solution, the objects bounce correctly at some angles. My biggest burden is the inverted Y-Axis, which makes me make mistakes with the adjustment of the angles.

In regards to the last part of your previous post, I just realized that I was talking nonsense. the distance is checked correctly.

8. Aug 2, 2015

### haruspex

For the arctan logic, you don't need so many cases.
I would start by defining a constant as the max value you allow as the result of a division, MaxReal, say.
Then your test for potential overflow is abs(Vx)*MaxReal < abs(Vy). When that returns true, angle=pi/2, else angle = atan (Vy/Vx).
Either way, you then adjust for negative Vx, if ((Vx)<0) angle += pi. That should be it.
Likewise for phi.

I see you have a problem with coliding objects getting stuck. I assume this is because as they try to separate after a collision they appear to collide again.
You could solve that by checking whether their relative velocity along the line of centres is bringing them closer or taking them further apart. With your existing method, seems to me you could have problems when there are multiple collisions in one time step.

9. Aug 2, 2015

### Vold

I managed to get it right with your input. I will now try to solve other problems related to it. The objects sometimes get stuck as you say, regardless, I am planning to have some animations that allow me to simulate some kind of 3D effect where if an object collides it can either go up or down or also get destroyed, etc. Still, I will try to fix it.
Thanks a lot! :D

Last edited: Aug 2, 2015