Forum Topic: As: Circle To Line Collisions

(1,100 views • 8 replies)

This topic is 1 page long.

<< < > >>
None

Rustygames

Reply To Post Reply & Quote

Posted at: 11/25/07 04:32 PM

Rustygames LIGHT LEVEL 18

Sign-Up: 05/07/05

Posts: 6,662

AS: Main

I recently needed to use this formula and this tutorial was misleading or just completely incorrect in a few places so I decided to quickly jot down how to actually do this.

The original topic can be found here.

To check if a line is colliding with a circle we find the distance between the center of the circle and any given point of the line (obviously the closest to the circle center). If that distance is less then the radius of the circle we know they must be touching.

To find the distance between a line and a point you use the following equation:

|ax+by+c| / sqrt(a^2+b^2)

Where:
|| denotes an absolute value
sqrt denotes the square root.
a = the graident of the line
b = -1 (I'll explain why in a second)
c = the intercept (offest) of a line
x = circle's x position
y = circle's y position

It might be necessary for me to remind you now that the circles x and y position should be the center of the circle. So either make sure the circle's registration point is in the center, or make the necessary adjustments to the x and y variables in that equation.

That's all you need to know. If you don't know how to find the equation of a line, read on:

The equation of a line is described by the following:

y = mx + c

This is basic high-school maths. If you don't know it then shame on you!

so we are interested in the m (the gradient of the line) and the c (the intercept [offset] of the line).

The gradient (m) is found by dividing any given change in y by any given change in x. Since we can find the width and height from a movieclip we can divide height by width to find the gradient.

Please note that this isnt entirely true, as height in flash is always positive. This means finding the graident this way only works for positive gradients. Which is a huge constraint. I havn't yet come up with a better solution then just telling each line whether it's positive or negetive at author time. If you come up with a better one, please share. Also note the height should be flipped to compensate for flash's co-ordinate system.

To find C we use the following equation:

c = y-mx

This is because y = mx+c and therefore taking mx from both sides created y-mx = c (basic algebra skills anyone?)

ok now when I told you what a b and c are above, I wasnt strictly telling the truth. Basically they are derived from a lines equation in the form of

ay + bx + c = 0

To find this we take away y from both sides which gives us:

-y + mx + c = 0

so as you can see, a = -1 and b = m.

As a final note please remember this tests a line assumed to be of infinate length. I don't know the math to find if it has hit a specific portion of a line, but my preliminary guess would be to find the distance between the center of the line and the circle and check if it's within the length you want. Though someone will probably come on and correct me any second now :D

Time for some code I think.

var a:Number = (-line_mc._height)/line_mc._width;
var b:Number = -1;
var c:Number = line_mc._y-line_mc._x*a;
onEnterFrame = function () {
	circle_mc._x = _xmouse;
	circle_mc._y = _ymouse;
	var dist:Number = Math.abs(a*circle_mc._x+b*circle_mc._y+c)/Math.sqrt(a*a+b*b);
	if (dist<circle_mc._width/2) {
		// Hit
	} else {
		// No Hit
	}
};

If any of the above is gibberish please say so and I'll try to explain. PM me for major questions or if you find any mistakes. Also i'd love to hear about how to solve the little gradient pickle.

Source File

- Matt, Rustyarcade.com


None

fwe

Reply To Post Reply & Quote

Posted at: 11/25/07 04:34 PM

fwe DARK LEVEL 08

Sign-Up: 07/24/03

Posts: 3,361

hey ninjachicken
how's the crumpets?

wtfbbqhax


None

Rustygames

Reply To Post Reply & Quote

Posted at: 11/25/07 04:35 PM

Rustygames LIGHT LEVEL 18

Sign-Up: 05/07/05

Posts: 6,662

At 11/25/07 04:34 PM, fwe wrote: hey ninjachicken
how's the crumpets?

Tastey as hell ;)

- Matt, Rustyarcade.com


None

dELtaluca

Reply To Post Reply & Quote

Posted at: 11/25/07 05:11 PM

dELtaluca LIGHT LEVEL 20

Sign-Up: 04/16/04

Posts: 5,548

to test for the intersection of a circle and a line segment, the easiest approach is through linear algebra, and you can approach it in two ways.

the first approach is to find the closest point on the line segment to the circle and test distances.
the second is to test directly for an intersection between the two objects:

for both ill use the following variables. A is the start of line segment, B the end, C the circle centre, and R the radius with V = B-A


----------------------------------------
-----------------

the first approach would go something like this:

first with a bit of intuition you can think of the closest point of the infinite line defined through A and B to C as being the point on the line at which the vector from this point to C is perpendicular to the line direction (i.e. the circle lies on a line perpendicular to the line segment)

i.e. the closest point is the solution to the equation:

(A + tV - C)%u25CFV = 0

which re-arranging gives:

(A-C)%u25CFV + tV%u25CFV = 0
t = (C-A)%u25CFV / |V|²

in terms of explicit 'x' and 'y' you have:

t = (Vx(Cx-Ax) + Vy(Cy-Ay))/(Vx² + Vy²)

since we only want to test against the line segment AB we have to cap t to the range 0-1 (so that if the closest point is any further than B, it uses B, and if its any further than A it uses A) i.e. if t is less than 0, equate it to 0, if its more than 1, equate it to 1.

the point to test with the circle is then A + tV, and the distance between this point and C is:

|A + tV - C|

for optimization, you needn't calculate the length of this vector, only its square length compared to the square of the circles radius. i.e.

if( |A + tV - C|² < R² ) // colliding

as an example code you might have something like:

function collideLineSegCircle(a:Point, b:Point, c:Point, r:Number):Boolean {
  var vx:Number = b.x-a.x;
  var vy:Number = b.y-a.y;
  var t:Number = (vx*(c.x-a.x) + v.y(c.y-a.y))/(vx*vx+vy*vy);
  if(t<0) t = 0;
  if(t>1) t = 1;
  var px:Number = a.x+vx*t-c.x;
  var py:Number = a.y+vy*t-c.y;
  return (px*px+py*py < r*r);
}


----------------------------------------
-----------------

the second method would go something like this:

the intersection of the line defined by AB and the circle is the solution to the quadratic equation derived from :

|A + tV - C|² = R²

|(A-C)+tV|² = R²
|A-C|² + t²|V|² + 2t(A-C)%u25CFV - R² = 0 // a quadratic equation in 't'

the real solution set of this equation can be used to calculate whether either intersection (if they exist in the reals) is on the line segment or not.

ultra-pseudo code :
calculate coeffecients of quadratic
calculate descrimininant, if its less than 0 end here.
calculate the 2 values for t, if either one of them is in the range 0-1, the line segment and circle intersect (collide)

as an example code you might have something like:

function collideLineSegCircle(a:Point, b:Point, c:Point, r:Number):Boolean {
  var vx:Number = b.x-a.x;
  var vy:Number = b.y-a.y;
  var dx:Number = a.x-c.x;
  var dy:Number = a.y-c.y;

  var c0:Number = vx*vx+vy*vy;
  var c1:Number = 2*(vx*dx+vy*dy);
  var c2:Number = dx*dx+dy*dy-r*r;

  var d:Number = c1*c1-4*c0*c2;
  if(d<=0) return false;
  d = Math.sqrt(d);
  c0 = 1/(2*c0);

  var t:Number = (d-c1)*c0;
  if(t>=0 || t<=1) return true;
  t = (-d-c1)*c0;
  return (t>=0||t<=1);
}

from examination of the code alone, it can be inferred that although the second method seems more complicated, it should execute faster.

My social worker says im special!

BBS Signature

None

dELtaluca

Reply To Post Reply & Quote

Posted at: 11/25/07 05:14 PM

dELtaluca LIGHT LEVEL 20

Sign-Up: 04/16/04

Posts: 5,548

At 11/25/07 05:11 PM, dELtaluca wrote: from examination of the code alone, it can be inferred that although the second method seems more complicated, it should execute faster.

FIRST i meant, sorry FIRST not second :P

also, fucking code tags don't display the %u25CF properly, they display it as %u25CF, sorry about that :/

My social worker says im special!

BBS Signature

None

dELtaluca

Reply To Post Reply & Quote

Posted at: 11/25/07 05:17 PM

dELtaluca LIGHT LEVEL 20

Sign-Up: 04/16/04

Posts: 5,548

blah, also reading through i made a typo in code for second, the last part should read:

if(t>=0 && t<=1) return true;
t = (-d-c1)*c0;
return (t>=0&&t<=1);

My social worker says im special!

BBS Signature

None

Fickludd

Reply To Post Reply & Quote

Posted at: 11/26/07 04:36 AM

Fickludd NEUTRAL LEVEL 15

Sign-Up: 02/28/04

Posts: 224

At 11/25/07 05:14 PM, dELtaluca wrote: also, fucking code tags don't display the %u25CF properly, they display it as %u25CF, sorry about that :/

Lol :D.

Good tutorials you both, but geometry's really much easier to follow and describe with pictures! And vectors!

page 50: AS:Main or for us progressing people: AS3:Main

BBS Signature

None

Rustygames

Reply To Post Reply & Quote

Posted at: 11/26/07 10:02 AM

Rustygames LIGHT LEVEL 18

Sign-Up: 05/07/05

Posts: 6,662

Thanks for adding to this delta. I havn't read it yet but I'll get my head around it soon ;)

- Matt, Rustyarcade.com


None

Ciph3rzer0

Reply To Post Reply & Quote

Posted at: 11/26/07 10:29 AM

Ciph3rzer0 DARK LEVEL 08

Sign-Up: 04/03/06

Posts: 545

At 11/26/07 10:02 AM, Rustygames wrote: Thanks for adding to this delta. I havn't read it yet but I'll get my head around it soon ;)

I have no idea what delta said... but it seems like you could use vector projection here...

(I'm sorry though I don't feel like getting into it right now.)

TimeGames.net - It's about time.

BBS Signature

All times are Eastern Standard Time (GMT -5) | Current Time: 05:02 AM

<< Back

This topic is 1 page long.

<< < > >>
You need a Grounds Gold Account to post on the NG BBS! If you don't have one, click here to sign up now! It's fast, free, and easy — and opens up tons of great NG features!