圆心:Cx,Cy
圆半径:a
我们需要绘制切线的点:Px,Py
根据上述所有内容,我需要公式来找到两条切线 (t1x, t1y) 和 (t2x,t2y)。
编辑: 有没有使用向量代数或其他东西的更简单的解决方案,而不是找到两条直线的方程,然后求解两条直线的方程以分别找到两条切线?另外这个问题并不是偏离主题,因为我需要编写一段代码来找到最佳答案
这是使用三角学的一种方法。如果您了解三角函数,这种方法很容易理解,尽管由于三角函数缺乏精确性,它可能无法给出准确的正确答案。
给出了点
C = (Cx, Cy)
和 P = (Px, Py)
,以及半径 a
。半径在我的图中显示了两次,即 a1
和 a2
。您可以轻松计算点 b
和 P
之间的距离 C
,并且可以看到线段 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)
有一些明显的方法可以组合这些计算并使它们更加优化,但我将把它留给你。还可以使用三角学的角度加法和减法公式以及一些其他恒等式来从计算中完全删除三角函数。然而,结果更加复杂且难以理解。未经测试,我不知道哪种方法更“优化”,但这取决于您的目的。如果您需要这种其他方法,请告诉我,但这里的其他答案无论如何都会为您提供其他方法。
请注意,如果
a > b
则 acos(a / b)
将抛出异常,但这意味着点 P
位于圆内并且没有切点。如果a == b
,则点
P
位于圆上,并且只有一个切点,即点
P
本身。我的代码适用于这种情况a < b
。我将让您对其他情况进行编码,并确定所需的精度来确定 a
和 b
是否相等。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
您可以利用您处于
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
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));
}
function tangentCircle (cx, cy, cr, px, py) -- circle, point
local dx = px-cx
local dy = py-cy
local dist = math.sqrt(dx*dx+dy*dy)
if dist <= cr then return end -- point inside circle
local a = math.atan2 (dy, dx)
local b = math.acos (cr/dist)
local x1 = cx + cr * math.cos (a-b)
local y1 = cy + cr * math.sin (a-b)
local x2 = cx + cr * math.cos (a+b)
local y2 = cy + cr * math.sin (a+b)
return x1, y1, x2, y2
end
示例:
print (tangentCircle (120, 100, 30, 20, 10))
-- result:
-- x1, y1: 95.463995562856 117.26222715238
-- x2, y2: 134.59125305593 73.787496604524