从一个点在圆中找到切点

Find tangent points in a circle from a point

圆心:Cx,Cy

圆半径:a

我们需要绘制切线的点:Px,Py

我需要公式来找到上述所有条件下的两条切线 (t1x, t1y) 和 (t2x,t2y)。

编辑:有没有使用矢量代数之类的更简单的解决方案,而不是找到两条直线的方程,然后求解两条直线的方程分别找到两条切线?这个问题也不是题外话,因为我需要写一个代码来找到这个问题

嗯,这不是真正的算法问题(人们往往会弄错算法和方程式)如果你想写代码就写(你没有指定语言,也没有什么阻止你这样做,这就是票数接近的原因) ...如果没有此信息,您的 OP 只是在询问数学方程式,这在这里确实是题外话,通过回答这个问题,我也冒着(完全)否决票的风险(但是这个 is/was 在这里问了很多很多更少的信息和 4 票反对 1 票关闭使我决定重新开放并无论如何回答这个问题)。

你可以利用你在 2D 中的事实,因为在 2D 垂直向量到向量 a(x,y) 的计算方式如下这个:

c = (-y, x)
d = ( y,-x)
c = -d

所以你交换 x,y 并取反(哪个决定垂直向量是 CW 还是 CCW)。这实际上是一个旋转公式,但当我们旋转 90 度时,cos,sin 只是 +1-1.

现在圆上任意圆周点的法线n位于通过该点和圆心的直线上。所以把所有这些放在一起你的切线是:

// normal
nx = Px-Cx
ny = Py-Cy
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx

如果你想要单位向量而不只是除以半径 a(不知道你为什么不像其他数学世界那样称呼它 r)所以:

// normal
nx = (Px-Cx)/a
ny = (Py-Cy)/a
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx

将圆移动到原点,旋转使点在X上并缩小R以获得单位圆。

现在当原点(0, 0)、(减少的)给定点(d, 0)和单位圆上的任意点(cos t, sin t)形成直角三角形时实现相切。

cos t (cos t - d) + sin t sin t = 1 - d cos t = 0

由此,你画出

cos t = 1 / d

sin t = ±√(1-1/d²).

获取初始几何体中的切点,upscale,unrotate和平移。 (这些是简单的线性代数运算。)请注意,无需显式执行直接变换。您只需要d,距离中心点与半径之比。

这是使用复数的另一种方法。 如果 a 是圆上切点从圆心 c 的方向(长度为 1 的复数),d 是沿切线到达 p 的(实数)长度,则(因为切线的方向是我*a)

p = c + r*a + d*I*a 

重新排列

(r+I*d)*a = p-c

但是 a 的长度为 1,所以取我们得到的长度

|r+I*d| = |p-c|

除了d我们什么都知道,所以我们可以求解d:

d = +- sqrt( |p-c|*|p-c| - r*r)

然后找到 a 和圆上的点,每个点对应上面的每个 d 值:

a = (p-c)/(r+I*d)
q = c + r*a

这是使用三角函数的一种方法。如果你了解三角函数,这种方法很容易理解,尽管由于三角函数缺乏准确性,它可能不会在可能的情况下给出准确的正确答案。

给出了点 C = (Cx, Cy)P = (Px, Py),以及半径 a。半径在我的图中显示了两次,分别为 a1a2。您可以轻松计算出点 PC 之间的距离 b,并且可以看到线段 b 形成了边长 a 的两个直角三角形的斜边.角度 theta(在我的图中也显示了两次)在斜边和相邻边 a 之间,因此可以用反余弦来计算。从点C到点P的向量的方向角也很容易通过反正切找到。切点的方向角为原方向角与计算出的三角角之和与差。最后,我们可以根据这些方向角和距离a来找到那些切点的坐标。

这是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)

有很明显的方法可以组合这些计算并使它们更优化一些,但我会把它留给你。也可以将三角学的角度加减公式与其他一些恒等式一起使用,以从计算中完全去除三角函数。然而,结果更复杂,更难理解。未经测试,我不知道哪种方法更 "optimized" 但这取决于您的目的。如果您需要这种其他方法,请告诉我,但这里的其他答案无论如何都会为您提供其他方法。

请注意,如果 a > b 那么 acos(a / b) 将抛出异常,但这意味着点 P 内部 圆没有切点。如果a == b则点P圆上只有一个切点,即点P本身。我的代码适用于案例 a < b。我将留给您编写其他情况的代码并确定所需的精度来确定 ab 是否相等。

让我们来看看推导过程:

如您所见,如果正方形的内部小于 0,那是因为点在圆周的内部。当点在圆周之外时,有两种解决方案,具体取决于正方形的符号。

剩下的就简单了。拿 atan(solution) 并注意这里的标志,你最好做一些检查。
使用 (2) 然后撤消 (1) 转换,仅此而已。

c#实现dmuir的回答:

static void FindTangents(Vector2 point, Vector2 circle, float r, out Line l1, out Line l2)
{
    var p = new Complex(point.x, point.y);
    var c = new Complex(circle.x, circle.y);
    var cp = p - c;
    var d = Math.Sqrt(cp.Real * cp.Real + cp.Imaginary * cp.Imaginary - r * r);
    var q = GetQ(r, cp, d, c);
    var q2 = GetQ(r, cp, -d, c);

    l1 = new Line(point, new Vector2((float) q.Real, (float) q.Imaginary));
    l2 = new Line(point, new Vector2((float) q2.Real, (float) q2.Imaginary));
}

static Complex GetQ(float r, Complex cp, double d, Complex c)
{
    return c + r * (cp / (r + Complex.ImaginaryOne * d));
}