Search code examples
javascriptcanvasgeometrydrawing

Find where two (tangent) lines intersect an enclosing circle


I have two concentric circles and two tangent lines originating from a (single) point on the outer circle. I want to determine where the extension of the tangent lines intersect the outer circle. If it matters, this is in JS, drawing on a canvas.

enter image description here

I know the coordinates and/or values for A, R, r, Tb, Tc, [and the center of the circles, of course, which we can assume is at (0, 0)]. I need to find the coordinates of "B" and "C".

Any help/suggestions welcome.


Solution

  • B.x = 2*Tb.x - A.x
    B.y = 2*Tb.y - A.y
    
    C.x = 2*Tc.x - A.x
    C.y = 2*Tc.y - A.y
    

    Solution when Tb, Tc are not known (as I thought at first...):

    At first we need to find tangent points T (TB and TC).

    Small circle radius vector to tangent point is perpendicular to tangent, so scalar product of these vectors is zero - eq.[1]. And this vector's length is r - eq.[2], so we have equation system:

    Tx*(Tx-Ax)+Ty*(Ty-Ay)=0       [1]
    Tx^2+Ty^2 = r^2               [2]
    

    Transforming it we get quadratic equation for unknown Tx coordinate

    Tx^2-Tx*Ax+Ty^2-TyAy=0
    r*r = Tx*Ax+TyAy
    Ty = (r^2 - Tx*Ax) / Ay            [3]
    Tx^2*Ay^2+(r^2 - Tx*Ax)^2 = r^2*Ay^2
    
    Tx^2*(Ay^2+Ax^2) + Tx*(-2*r^2*Ax) + (r^4-r^2*Ay^2) = 0   [4]
    

    Solving the last quadratic equation we get two (in general case) solutions for coordinates TBx, TCx, then calculate Y-coordinates from [3] if Ay!=0 or from [2] otherwise.

    A-B vector is just doubled A-TB vector, so

    Bx = Ax + 2*(TBx-Ax) = 2*TBx - Ax
    By = Ay + 2*(TCy-Ay) = 2*TBy - Ay
    

    and similar for C point coordinates

    Python code for reference:

    def square_eq(a, b, c):
        Dis = b*b-4*a*c
        if Dis < 0:
            return []
        elif Dis ==0:
            return [-0.5*b/a]
        else:
            Dis = Dis**0.5
            return [0.5*(-b-Dis)/a, 0.5*(-b+Dis)/a]
    
    def twocirctan(ax, ay, r):
        a = ay*ay+ax*ax
        if a <= r*r:
            return None
        b = -2*r*r*ax
        c = r**4-r*r*ay*ay
        sol = square_eq(a, b, c)
        if len(sol)==0:
            return None
        elif len(sol)==1:
            tbx = sol[0]
            tcx = sol[0]
            tby = (r*r - tbx*tbx)**0.5
            tcy = -tby
        else:
            tbx = sol[0]
            tcx = sol[1]
            tby = (r*r - tbx*ax) / ay
            tcy = (r*r - tcx*ax) / ay
    
        return [[2*tbx-ax, 2*tby-ay], [2*tcx-ax, 2*tcy-ay]]
    
    print(twocirctan(1,0,2))
    print(twocirctan(5,0,2))
    print(twocirctan(3,4,2))
    

    Results:

    None
    [[-3.4, 3.666060555964672], [-3.4, -3.666060555964672]]
    [[-4.972848444771738, -0.520363666421197], [0.8928484447717375, -4.919636333578803]]
    

    And as a JavaScript snippet:

    const square_eq = (a, b, c) => {
      let dis = b*b-4*a*c;
      if (dis < 0) {
        return [];
      }
      else if (dis === 0) {
        return [-0.5*b/a];
      }
      else {
        dis = dis ** 0.5;
        return [0.5*(-b-dis)/a, 0.5*(-b+dis)/a];
      }
    }
    
    const twocirctan = (ax, ay, r) => {
      const a = ay*ay+ax*ax;
      if (a <= r*r) {
        return NaN;
      }
      const b = -2*r*r*ax;
      const c = r**4-r*r*ay*ay;
      const sol = square_eq(a, b, c);
      let tbx, tcx, tby, tcy;
      if (sol.length === 0) {
        return NaN;
      }
      else if (sol.length === 1) {
        tbx = sol[0];
        tcx = sol[0];
        tby = (r*r - tbx*tbx)**0.5;
        tcy = -tby;
      }
      else {
        tbx = sol[0];
        tcx = sol[1];
        tby = (r*r - tbx*ax) / ay;
        tcy = (r*r - tcx*ax) / ay;
      }
      return [[2*tbx-ax, 2*tby-ay], [2*tcx-ax, 2*tcy-ay]];
    }
    
    console.log(twocirctan(1,0,2))
    console.log(twocirctan(5,0,2))
    console.log(twocirctan(3,4,2))

    Proof picture (B,C,E,F coordinates):

    enter image description here