给出了点 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)
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));
}
6 回答
这是使用复数的另一种方式 . 如果a是中心c上圆上切点的方向(长度为1的复数),d是沿切线得到p的(实际)长度,那么(因为切线的方向是我* A)
重排
但是长度为1,所以我们得到的长度
我们知道除了d之外的一切,所以我们可以解决d:
然后找到圆圈上的a和圆点,上面每个d的值各占一个:
这是使用三角法的一种方法 . 如果你理解trig,这个方法很容易理解,尽管由于trig函数缺乏准确性,它可能无法在可能的情况下给出正确的答案 .
给出了点
C = (Cx, Cy)
和P = (Px, Py)
,以及半径a
. 半径在我的图表中显示两次,如a1
和a2
. 您可以轻松计算点P
和C
之间的距离b
,您可以看到段b
形成两个直角三角形的斜边,边为a
. 角度theta
(在我的图中也显示两次)位于斜边和相邻边a
之间,因此可以使用反余弦来计算 . 通过反正切也很容易找到从点C
到点P
的向量的方向角 . 相切点的方向角是原始方向角和计算的三角形角的和与差 . 最后,我们可以使用那些方向角和距离a
来找到那些相切点的坐标 .这是Python 3中的代码 .
有明显的方法可以将这些计算结合起来并使它们更加优化,但我会留给您 . 也可以使用具有一些其他身份的三角学的角度加法和减法公式来从计算中完全去除trig函数 . 但是,结果更复杂且难以理解 . 如果没有测试,我不知道哪种方法更“优化”,但这取决于您的目的 . 如果您需要这种其他方法,请告诉我,但其他答案在这里为您提供了其他方法 .
请注意,如果
a > b
然后acos(a / b)
将抛出异常,但这意味着P
点位于圆内并且没有相切点 . 如果a == b
然后点P
在圆上并且只有一个相切点,即点P
本身 . 我的代码适用于案例a < b
. 我将留给你编码其他情况并决定所需的精度来决定a
和b
是否相等 .嗯,这不是一个算法问题(人们往往会误解算法和方程式)如果你想写一个代码然后做(你没有指定语言,也没有什么阻止你这样做,这就是关闭投票的原因)...没有这个信息你的OP只是要求数学等式,这确实是偏离主题的,并且通过回答这个风险(右 - 完全)向下投票(但这是/在这里被问了很少的信息和4次重新开始投票反对1关闭我的决定权重重新打开并回答这个问题) .
你可以利用你在 2D 中的事实,如 2D 垂直向量向量
a(x,y)
计算如下:所以你交换
x,y
并否定一个(哪一个确定垂直向量是 CW 还是 CCW ) . 它实际上是一个旋转公式但是当我们旋转90度时,cos,sin
只是+1
和-1
.现在正常的圆圈上的任何圆周点都位于穿过该点并且圆心的线上 . 所以把所有这些放在一起你的切线是:
如果你想要单位向量而不是除以半径
a
(不知道为什么你不称它为r
,如同其余的数学世界)所以:让我们来完成推导过程:
正如您所看到的,如果正方形的内部<0,那是因为该点位于圆周的内部 . 当该点在圆周之外时,根据方形的符号,有两种解决方案 .
其余的很容易 . 拿
atan(solution)
并注意这些标志,你最好做一些检查 .使用(2)然后撤消(1)转换,这就是全部 .
c#执行dmuir的答案:
将圆圈移动到原点,旋转以使
X
上的点和R
缩小以获得单位圆 .现在,当原点
(0, 0)
,(缩小的)给定点(d, 0)
和单位圆(cos t, sin t)
上的任意点形成直角三角形时,实现相切 .从中,你画画
和
获取初始几何中的相切点,高档,不旋转和不翻译 . (这些是简单的线性代数运算 . )请注意,不需要显式执行直接变换 . 您所需要的只是
d
,距离中心点与半径之比 .