Here is one way using trigonometry. If you understand trig, this method is easy to understand, though it may not give the exact correct answer when one is possible, due to the lack of exactness in trig functions.

The points C = (Cx, Cy)
and P = (Px, Py)
are given, as well as the radius a
. The radius is shown twice in my diagram, as a1
and a2
. You can easily calculate the distance b
between points P
and C
, and you can see that segment b
forms the hypotenuse of two right triangles with side a
. The angle theta
(also shown twice in my diagram) is between the hypotenuse and adjacent side a
so it can be calculated with an arccosine. The direction angle of the vector from point C
to point P
is also easily found by an arctangent. The direction angles of the tangency points are the sum and difference of the original direction angle and the calculated triangle angle. Finally, we can use those direction angles and the distance a
to find the coordinates of those tangency points.
Here is code in Python 3.
# Example values
(Px, Py) = (5, 2)
(Cx, Cy) = (1, 1)
a = 2
from math import sqrt, acos, atan2, sin, cos
b = sqrt((Px - Cx)**2 + (Py - Cy)**2) # hypot() also works here
th = acos(a / b) # angle theta
d = atan2(Py - Cy, Px - Cx) # direction angle of point P from C
d1 = d + th # direction angle of point T1 from C
d2 = d - th # direction angle of point T2 from C
T1x = Cx + a * cos(d1)
T1y = Cy + a * sin(d1)
T2x = Cx + a * cos(d2)
T2y = Cy + a * sin(d2)
There are obvious ways to combine those calculations and make them a little more optimized, but I'll leave that to you. It is also possible to use the angle addition and subtraction formulas of trigonometry with a few other identities to completely remove the trig functions from the calculations. However, the result is more complicated and difficult to understand. Without testing I do not know which approach is more "optimized" but that depends on your purposes anyway. Let me know if you need this other approach, but the other answers here give you other approaches anyway.
Note that if a > b
then acos(a / b)
will throw an exception, but this means that point P
is inside the circle and there is no tangency point. If a == b
then point P
is on the circle and there is only one tangency point, namely point P
itself. My code is for the case a < b
. I'll leave it to you to code the other cases and to decide the needed precision to decide if a
and b
are equal.