See the question and my original answer on StackOverflow

Here is a C# solution (it can be easily adapted to other languages) that doesn't use trigonometric functions, only geometric construction.

You can see a living representation here (P, C and radius can be moved): https://www.desmos.com/calculator/ifzt2nzyl9

enter image description here

public static double[] GetTangentPoints(
    double px,
    double py,
    double cx,
    double cy,
    double radius)
{
    var points = new double[4];

    var dx = cx - px;
    var dy = cy - py;
    if (dx == 0 && dy == 0)
        return null; // no solution

    // PC is distance between P and C, pc2 is PC^2
    var pc2 = dx * dx + dy * dy;
    var pc = Math.Sqrt(pc2);
    if (pc < radius)
        return null; // no solution

    // R is radius of  circle centered in P, r2 is R^2
    var r2 = pc2 - radius * radius;

    // d is the P => X0 distance (demonstration is here https://mathworld.wolfram.com/Circle-CircleIntersection.html where PC is named 'd' in there)
    var d = r2 / pc;

    // h is the X0 => X1 (and X0 => X2) distance
    var h = Math.Sqrt(r2 - d * d);

    // first tangent point
    points[0] = px + (dx * d - dy * h) / pc;
    points[1] = py + (dy * d + dx * h) / pc;

    // second tangent point
    points[2] = px + (dx * d + dy * h) / pc;
    points[3] = py + (dy * d - dx * h) / pc;

    return points;
}