# Circle vs rectangle corner collision

This topic is 3949 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi all, So I'm trying to do some collision physics and I'm currently working with a circle adn a rectangle. I also want to preserve kinetic energi after collision so the two objects have finite masses. When the circle/ball collides with one of the four sides of the rectangle everything seems to be fine. I do have some problem with the corners though. Here's how I was thinking of solving this: When the circle touches a corner, I use the line between the corner and the middle of the circle to create a normal. Then I use the velocity vector for the collision, decompose it so I get a component along the normal and one perpendicular to it. I do the same with the velocity vector for the rectangle and then I use the two vector components which lie along the normal and use them to collide the two objects as you would with a one dimensional collision. I think this should give a somewhat realistic bounce when the two objects collide, but what I fear is that my code is really messed up. So I would love if someone has the time and take a look at it and give me some feedback what could be wrong. I've been sitting with this for quite some time and you will notice that the further down into the code you get, the more messed up it is. But it's still just a sketch so I blame it on that. I just want the damn collisions to work =) Here's the code for when the ball collides with the top left corner of the rectangle. I think I basically have the same code for all the corners except for when two/three lines where the normal is calculated (since x_pos, y_pos are in the middle of the rectangle). I also got an applet up with this code, you can find it here and see what happens when collisions occur. If you reload the page it should generate new speeds and dimensions for the objects. If you don't wanna look at the applet what happens is that the ball kinda (sometimes) gets stuck inside the rectangle, when it collides with a corner, of course. I think it might have something to do that all the trigonometry with the angles loses what quadrant the angle should be in, or something. Well any help would be greatly appreciated. And the code...
//top left cornet
if (getDistance(ball_x, ball_y, rect_x-rect_w/2, rect_y-rect_h/2) < ball_r)
{
//change color to indicate collision
ball.setColor(Color.blue);

//stupid variables for the dot product
double pt1 = ball_vx;
double pt2 = ball_vy;
double pt3 = (rect_x - rect_w/2) - ball_x; //this is the collision normal, a line form the middle of the circle to the corner
double pt4 = (rect_y - rect_h/2) - ball_y;

//dot product to find angle between ball's velocity vector and collision normal
double ai = ((pt1*pt3) + (pt2*pt4)) / ((Math.sqrt(pt1*pt1+pt2*pt2)) * (Math.sqrt(pt3*pt3+pt4*pt4)));
double i = Math.acos(ai);

//rotate the vector (OBS. might need some tweaking for the different cases if something shows up)
/*double xp = Math.cos(2*i)*ball_vx - Math.sin(2*i)*ball_vy; //detta ska också ändras för andra fall
double yp = Math.sin(2*i)*ball_vx + Math.cos(2*i)*ball_vy;

//set final velocity vector
ball.setVelX(xp);
ball.setVelY(yp);*/

//this is the velocity vector for the ball projected on the normal
double bcv = ball_vec * Math.cos(i);

//use dot product to find the angle between the rect's velocity vector and the normal
pt1 = rect_vx;
pt2 = rect_vy;
double air = ((pt1*pt3) + (pt2*pt4)) / ((Math.sqrt(pt1*pt1+pt2*pt2)) * (Math.sqrt(pt3*pt3+pt4*pt4)));
double ir = Math.acos(air);

//the rect's velocity vector projected on the normal
double rcv = rect_vec * Math.cos(ir);

//calculate the velocities after collision
double ball_final = bcv * ((ball_m-rect_m)/(ball_m+rect_m)) + rcv * ((2*rect_m)/(ball_m+rect_m));
double rect_final = bcv * ((2*ball_m)/(ball_m+rect_m)) - rcv * ((ball_m-rect_m)/(ball_m+rect_m));

//now we have the two final vectors on the normal. these need to be used together with the remaining components from the initial velocity vectors
//to get the final velocity vectors This should be correct in theory, I hope, but I fear the code bellow is messed up.

//first the ball
double remaining_component = Math.sin(i) * ball_vec; //get the remaining component from the initial velocity vector (i.e. the one not colliding)
double ball_f = Math.sqrt(remaining_component*remaining_component + ball_final*ball_final); //the final velocity vector
//calculate the new angle between the new velocity vector and the normal
double new_i = Math.atan(ball_final / remaining_component);
//calculate angle between normal and x-axis
double norm_a = Math.atan(pt4/pt3);
//angle between velocity vector and x-axis
double final_a = norm_a - new_i; //this i'm very uncertain about
//set the speeds
ball.setVelX(ball_f * Math.cos(final_a));
ball.setVelY(ball_f * Math.sin(final_a));

//now the rectangle
remaining_component = Math.sin(ir) * rect_vec; //get the remaining component from the initial velocity vector (i.e. the one not colliding)
double rect_f = Math.sqrt(remaining_component*remaining_component + rect_final*rect_final); //the final velocity vector
//calculate the new angle between the new velocity vector and the normal
new_i = Math.atan(rect_final / remaining_component);
//angle between velocity vector and x-axis
final_a = norm_a + new_i;
//set the speeds
rect.setVelX(rect_f * Math.cos(final_a));
rect.setVelY(rect_f * Math.sin(final_a));
}



##### Share on other sites
Check if the objects are already moving apart before applying the collision forces. What happens is the objects collide, their velocities update so they are separating, but the velocities were not high enough to separate in one frame. The next frame they are already moving apart but still overlapping so they collide again, which applies forces that pushes the objects towards each other.

##### Share on other sites
That was a good point...and probably why the objects got stuck together. I solved this very fast by moving the ball out from the rectangle along the normal 10 pixels. Now they don't get stuck anymore at least, but if you watch the collisions, they're not correct. Or actually, I think they are but it looks like the angles swap in some weird way.

Could anyone take a look at the trigonometry in my code above and see where the error might be? I'm looking for it non-stop but maybe someone else is faster. It's alot to ask to go through the code above but maybe someone has time =)

Here's the applet again -> APPLET
When the ball collides with a side of the rectangle it flashes green and when it collides with a corner it flashes blue.

##### Share on other sites
I've done some small corrections to the code where I had to invert tan(dx/dy) to tan(dy/dx) in some places and such...It's still not totally correct though. If anyone wants to watch the uppdated applet above, you can see that in some cases the bounces are actually correct. It looks like the calculations aren't generalized...

Here's the new code if anyone's interested
//top left cornet		if (getDistance(ball_x, ball_y, rect_x-rect_w/2, rect_y-rect_h/2) < ball_r)		{			//change color to indicate collision			ball.setColor(Color.blue);			//stupid variables for the dot product			double pt1 = ball_vx;			double pt2 = ball_vy;			double pt3 = (rect_x - rect_w/2) - ball_x; //this is the collision normal, a line form the middle of the circle to the corner			double pt4 = (rect_y - rect_h/2) - ball_y;						//dot product to find angle between ball's velocity vector and collision normal			double ai = ((pt1*pt3) + (pt2*pt4)) / ((Math.sqrt(pt1*pt1+pt2*pt2)) * (Math.sqrt(pt3*pt3+pt4*pt4)));			double i = Math.acos(ai);			//this is the velocity vector for the ball projected on the normal			double bcv = ball_vec * Math.cos(i);			//use dot product to find the angle between the rect's velocity vector and the normal			pt1 = rect_vx;			pt2 = rect_vy;			double air = ((pt1*pt3) + (pt2*pt4)) / ((Math.sqrt(pt1*pt1+pt2*pt2)) * (Math.sqrt(pt3*pt3+pt4*pt4)));			double ir = Math.acos(air);			//the rect's velocity vector projected on the normal			double rcv = rect_vec * Math.cos(ir);			//calculate the velocities after collision			double ball_final = bcv * ((ball_m-rect_m)/(ball_m+rect_m)) + rcv * ((2*rect_m)/(ball_m+rect_m));			double rect_final = bcv * ((2*ball_m)/(ball_m+rect_m)) - rcv * ((ball_m-rect_m)/(ball_m+rect_m));			//now we have the two final vectors on the normal. these need to be used together with the remaining components from the initial velocity vectors			//to get the final velocity vectors This should be correct in theory, I hope, but I fear the code bellow is messed up.			//first the ball			double remaining_component = Math.sin(i) * ball_vec; //get the remaining component from the initial velocity vector (i.e. the one not colliding)			double ball_f = Math.sqrt(remaining_component*remaining_component + ball_final*ball_final); //the final velocity vector			//calculate the new angle between the new velocity vector and the normal			double new_i = Math.atan(remaining_component / ball_final); //OBSOBS switched places for these two guys			//calculate angle between normal and x-axis			double norm_a = Math.atan(pt4/pt3);			//angle between velocity vector and x-axis			double final_a = new_i - norm_a; //OBS swiched places			//set the speeds			ball.setVelX(ball_f * Math.cos(final_a));			ball.setVelY(ball_f * Math.sin(final_a));			//now the rectangle			remaining_component = Math.sin(ir) * rect_vec; //get the remaining component from the initial velocity vector (i.e. the one not colliding)			double rect_f = Math.sqrt(remaining_component*remaining_component + rect_final*rect_final); //the final velocity vector			//calculate the new angle between the new velocity vector and the normal			new_i = Math.atan(remaining_component / rect_final); //Switched places			//angle between velocity vector and x-axis			final_a = new_i -  norm_a;			//set the speeds			rect.setVelX(rect_f * Math.cos(final_a));			rect.setVelY(rect_f * Math.sin(final_a));		}

##### Share on other sites
if you want generalised calculations, you should really use vector maths. That bounce stuff can be fix in a few lines of code. No trig either.

Here is the generalised response code.

Here is the box/circle detection, along with the corner detection.

the whole code :

// Find the closest point on the box surface to a pointVector ClosestPointOnBox(const Vector& Point, const Vector& Centre, const Vector& Extent, bool& inside){    Vector Delta = (Point - Centre);    inside = true;       if (fabs(Delta.x) > Extent.x)      {        inside = false;        Delta.x = Extent.x * sign(Delta.x);    }    if (fabs(Delta.y) > Extent.y)      {        inside = false;        Delta.y = Extent.y * sign(Delta.y);    }    if (fabs(Delta.z) > Extent.z)      {        inside = false;        Delta.z = Extent.z * sign(Delta.z);    }    // point was found outside. all good.    if(!inside)        return Centre + Delta;    // find the MTD (Minimum Translation Distance).    Vector MTD;    // calculate the distance of the point form one face along each axes.    MTD.x = (Extent.x - fabs(Delta.x)) * sgn(Delta.x);    MTD.y = (Extent.y - fabs(Delta.y)) * sgn(Delta.y);    MTD.z = (Extent.z - fabs(Delta.z)) * sgn(Delta.z);    // Find the minimum of the three.    if (fabs(MTD.x) < fabs(MTD.y))    {        MTD.y = 0.0f;        if (fabs(MTD.z) < fabs(MTD.x))            MTD.x = 0.0f;        else             MTD.z = 0.0f;    }    else     {        MTD.x = 0.0f;        if (fabs(MTD.z) < fabs(MTD.y))            MTD.y = 0.0f;        else             MTD.z = 0.0f;    }    // point on surface    return Point + MTD;}

// Resolve a collision, using the conservation of momentumbool ResolveCollision(const Vector& N, Vector& Va, float ima, Vector& Vb, float imb, float CoR=0.8f, float CoF=0.05f){    // relative velocities    Vector V = (Va - Vb);    float vn = V.DotProduct(N); // velocity along normal, or impact velocity    if (vn > 0.0f) return false; // objects moving away from each other.     //-------------------------------------------------    // Collision Impulse    //-------------------------------------------------    float i = (-(1.0f + CoR) * vn / (ima + imb)); // collision impulse.    Vector Ir = N * i; // vector collision impulse    Va += Ir * ima;     Vb -= Ir * imb;    //-------------------------------------------------    // Friction Impulse    //-------------------------------------------------    Vector Vt = V - (vn * N); // velocity projected in plane of collision    Vector If = Vt * (-CoF / (ima + imb)); // friction impulse    Va += If * ima;    Vb -= If * imb;    return true;}

// Solve the intersection between two embedded objectsbool ResolveIntersection(const Vector& N, float depth, Vector& Ca, float ima, Vector& Cb, float imb, float relaxation=0.5f){    Ca += N * depth * (ima / (ima + imb)) * relaxation;    Cb -= N * depth * (imb / (ima + imb)) * relaxation;    return true;}

// Compute the collision between a box and a sphere// 1) Detect intersection// 2) Solve intersection// 3) Solve collision impulsebool CollideSphereBox(C_Sphere& S, C_Box& B){    // Find the closest point on box to the sphere centre    bool inside;    Vector Pclosest = ClosestPointOnBox(S.m_Centre, B.m_Centre, B.m_HalfSize, inside);    // Check if closest point is inside the sphere    Vector Delta = (S.m_Centre - Pclosest);    float delta_length_squared = Delta.DotProduct(Delta);    // Sphere and box dont intersect    if(delta_length_squared > S.m_Radius * S.m_Radius) return false;    // Generate the collision plane information    float delta_length = sqrt(delta_length_squared);    Vector Ncoll;    float dcoll;    // Ball centre was inside the box.     // The intersection is deep.    if(inside)        delta_length *= -1.0f;    Ncoll = Delta / delta_length;    dcoll = S.m_Radius - delta_length;        // resolve the intersection    ResolveIntersection(Ncoll, dcoll, S.m_Centre, S.m_InverseMass, B.m_Centre, B.m_InverseMass);    // resolve the collision    ResolveCollision(Ncoll, S.m_Velocity, S.m_InverseMass, B.m_Velocity, B.m_InverseMass);    return true;}

[Edited by - oliii on March 25, 2007 1:56:37 PM]

##### Share on other sites
Demo

[Edited by - oliii on March 26, 2007 2:51:54 AM]

##### Share on other sites
yeah, I don't know what's going on. I can't seem to access anything from my account... :/

hmm that's interesting. I posted after you, and my post is inserted before yours. GDnet gremlins on a rampage...

##### Share on other sites
Thanx for the reply. Looks really great. I'm having a hard time understanding all the code but I'll probably get through it. Would love to see that demo but it doesn't seem to work.

##### Share on other sites
here's the full source. you will need glfw (gl framework).