2
votes

I have a circle with origin at (cx, cy). Radius is r. Then, there is a line segment defined by two points: (x1,y1) and (x2,y2). How to determine if the line segment (not the extended line) is tangent to the circle? And if yes, where do the two touch?

What I am doing now: find out the distance of the point (cx, cy) from the extended line. If the distance != r, then certainly the line segment is NOT tangent to circle. Even if the distance == r, then we need to find out the point where they touch. Then check whether that point lies on the segment between (x1,y1) and (x2,y2). If yes, the line segment IS tangent to the circle - and the touch point is computed already. This works but involves too much math. And all with float or double variables. Isn't there a smarter, faster algorithm to achieve same result?

Thanks and regards, Pramod

3
Can you post the code you're using now? Maybe your appraoch is ok but the code can be optimised.m69 ''snarky and unwelcoming''
I am afraid, I can't "post the code", since it is on paper. And it is only pseudo code.Pramod

3 Answers

3
votes

I recommend you stop reasoning with slopes because the singularity at the vertical is always nasty to deal with. Try instead the parametric form:

p = p1 + t v   where   v = p2 - p1

Now project the vector p1 - c onto v, take the derivative wrt t, set to zero, and you quickly have an expression for the value of t that describes the point on the infinite line closest to c, which is the tangent point:

    (c - p1) dot v
t = --------------
       v dot v

If this value is between 0 and 1, then the tangent point is between p1 and p2. This is a pretty cheap computation. When it's true, you can follow up with a radius check

(c - p1 - tv) dot (c - p1 - tv) ~= r^2  ?

Note the sub-term c - p1 is already calculated above.

You mentioned only the circle is moving, so you can compute v dot v once and save it.

2
votes

You maybe aware of the property that if f(x,y) = y-m*x-c is the line segment then |f(x1,y1)|/sqrt(1+m^2) represents the distance of the line from (x1,y1). Hence:

double m = (y2-y1)/(x2-x1);//slope
double c = y1 - m*x1;//since (x1,y1) lies on the line f(x1,y1) is zero
double d = abs(cy - m*cx - c)/sqrt(1+m*m);//distance
if(d==r)//radius
 //Yeah its tangent and do whatever you want
else
 //Nope

And for the second part,pseudocode;

g1(x,y) = y+(1/m)*x-c1;//perpendicular line through (x1,y1)
g2(x,y) = y+(1/m)*x-c2;//perpendicular line through (x2,y2)
c1 = y1+(1/m)*x1;
c2 = y2+(1/m)*x2;
if(g1(cx,cy)*g2(cx,cy)<0)//condition if point lies between two lines.Here make sure the coeffecients of y and x are of same sign in g1 and g2
//yes
else
//no
0
votes

By definition, a tangent line must be perpendicular to a radius line. In the picture below, the red line is a tangent line if, and only if, it's perpendicular to the green line (from the center to the point of contact).

enter image description here

So if you know the slope of the tangent line M (computed from (x1,y1) and (x2,y2)) then the slope of the radius line is -1/M. Given that you know

  • the center of the circle
  • the radius of the circle
  • the slope of the green line

it's easy to compute the point of contact. Actually there are two possible points of contact, on opposite sides of the circle.

So all you need to do is check whether either of the two possible points of contact are on the line segment.