Algorithm 从一个点查找圆中的切点
圆心:Cx,Cy 圆半径:a 我们需要画一条切线的点:Px,Py 我需要一个公式来找到上面给出的两条切线(t1x,t1y)和(t2x,t2y)Algorithm 从一个点查找圆中的切点,algorithm,math,geometry,algebra,Algorithm,Math,Geometry,Algebra,圆心:Cx,Cy 圆半径:a 我们需要画一条切线的点:Px,Py 我需要一个公式来找到上面给出的两条切线(t1x,t1y)和(t2x,t2y) 编辑:有没有更简单的解决方案,使用向量代数或其他方法,而不是先求两条直线的方程,然后再解两条直线的方程,分别求出两条切线?同样,这个问题也不是离题的,因为我需要编写一个代码,以找到最佳的嗯,这不是一个真正的算法问题(人们往往会误解算法和方程),如果你想编写一个代码,那么就这样做(你没有指定语言,也没有指定什么阻止你这样做,这是投票结果接近的原因)。。。如
编辑:有没有更简单的解决方案,使用向量代数或其他方法,而不是先求两条直线的方程,然后再解两条直线的方程,分别求出两条切线?同样,这个问题也不是离题的,因为我需要编写一个代码,以找到最佳的嗯,这不是一个真正的算法问题(人们往往会误解算法和方程),如果你想编写一个代码,那么就这样做(你没有指定语言,也没有指定什么阻止你这样做,这是投票结果接近的原因)。。。如果没有这些信息,你的OP只是要求数学方程式,这确实是离题的,回答这个问题,我也会冒(右满)否决票的风险(但这个问题在这里被问了很多次,信息要少得多,4票重新开放,1票关闭,我的决定权在重新开放和回答这个问题上) 您可以利用这样一个事实,即您处于2D中,正如在2D中,垂直于向量的向量
a(x,y)
的计算如下:
c = (-y, x)
d = ( y,-x)
c = -d
因此,交换x,y
并取反(哪一个确定垂直向量是CW还是CCW)。这实际上是一个旋转公式,但当我们旋转90度时,cos,sin
就是+1
和-1
现在,圆上任何圆周点的法线位于穿过该点和圆中心的直线上。所以把所有这些放在一起,你的切线是:
//正常
nx=Px-Cx
ny=Py-Cy
//切线1
tx=-ny
ty=+nx
//切线2
tx=+ny
ty=-nx
如果你想要单位向量,而不是仅仅除以半径a
(不知道为什么你不把它称为r
,就像其他数学世界一样),那么:
//正常
nx=(Px-Cx)/a
ny=(Py-Cy)/a
//切线1
tx=-ny
ty=+nx
//切线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
及
要获取初始几何体中的切点,请选择“高比例”、“不旋转”和“不平移”。(这些是简单的线性代数运算。)请注意,不需要显式执行直接变换。您只需要d
,距离中心点与半径的比率
这里有另一种使用复数的方法。 如果a是圆上切点从中心c开始的方向(长度1的复数),d是沿着切线到达p的(实际)长度,那么(因为切线的方向是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
这里有一种使用三角学的方法。如果您了解trig,这种方法很容易理解,尽管由于trig函数的不精确性,在可能的情况下,它可能无法给出准确的答案 给出了点
C=(Cx,Cy)
和p=(Px,Py)
,以及半径a
。半径在我的图表中显示了两次,分别为a1
和a2
。您可以轻松计算点P
和C
之间的距离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)
有一些明显的方法可以将这些计算结合起来,使它们更加优化,但我将把这留给你们。也可以使用三角函数的角度加减公式和其他一些恒等式,从计算中完全删除三角函数。然而,结果更为复杂和难以理解。没有测试,我不知道哪种方法更“优化”,但这取决于您的目的。如果您需要其他方法,请告诉我,但这里的其他答案会为您提供其他方法
请注意,如果
a>b
,则acos(a/b)
将抛出异常,但这意味着点p
位于圆内,没有切点。如果a==b
则点P
位于圆上,并且只有一个切点,即点P
本身。我的代码用于案例a
。我将让您对其他情况进行编码,并确定所需的精度,以确定a
和b
是否相等。让我们来完成推导过程:
正如你所看到的,如果正方形的内部小于0,那是因为该点位于圆周的内部。当点位于圆周外时,根据正方形的符号,有两种解决方案。
剩下的很简单。拿着atan(解决方案)
仔细看这里的标志,你可能会更好
a = (p-c)/(r+I*d)
q = c + r*a
# 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)
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));
}