给定一条具有第一个端点 P(x1,y1) 的线,另一个端点是未知的,与位于原点的圆相交,半径 R 仅在一个点 (切线) T(x2,y2) 处。有谁知道如何获得T点?提前致谢!
10 回答
给定一条具有第一个端点 P(x1,y1) 的线,另一个端点是未知的,与位于原点的圆相交,半径 R 仅在一个点 (切线) T(x2,y2) 处。有谁知道如何获得T点?
其他一些解决方案似乎有点矫枉过正。我认为最简单的方法就是注意这是一个直角三角形,有顶点 P、T 和 O(原点)。角度 PTO 是直角,因为切线始终与半径成直角。
您知道 的长度,TO
因为它是有长度的r
并且在原点有一个顶点;你知道OP
,因为你知道在哪里O
和在哪里P
。给定直角三角形的两条边,很容易找到第三条边的长度和方向。这是家庭作业,所以我将把剩下的留给读者作为练习。
__...------__ T(x2, y2)
_.-'' -(+)
,-' |----
,' | ----
,' | ' ----
/ | ` ----
/ | `. ----
/ | \ ----
| | | ----
| | | ----
| | | ----
| (+)---------------------------------------------(+) P (x1,y1)
| .'
| O |
| .'
\ /
\ ,'
` /
'. ,'
'-. _,'
'-._ _,(+) T'(x3, y3)
'`--......---'
有两个可能的方向TO
,因为点 T' 也是一个有效的切点,所以你将有两个全等三角形。
您所需要的只是 dmckee 的答案,但如果您关心一些代码,请使用 Javascript 和 HTML 画布检查此实现。
完整示例:http: //jsfiddle.net/zxqCw/1/
// find tangents
dx = cx - px;
dy = cy - py;
dd = Math.sqrt(dx * dx + dy * dy);
a = Math.asin(radius / dd);
b = Math.atan2(dy, dx);
t = b - a
ta = { x:radius * Math.sin(t), y:radius * -Math.cos(t) };
t = b + a
tb = { x:radius * -Math.sin(t), y:radius * Math.cos(t) };
取R
圆的半径和D
外点到圆心的距离,使得D > R
.
切线\alpha
与连接外点和中心的线的夹角和角度,其中
\alpha = arcsin(R/D)
连接外点 ( P
) 和中心 ( C
) 的线与水平线成角度
\beta = arctan((C_y - P_y)/(C_x - P_x))
这给了你切线与水平线的角度为
\theta = \beta +/- \alpha
注意歧义。
切线段的长度为
L = sqrt(D^2 - R^2)
这就是你所需要的。
imbrizi 的答案假设圆的中心是 (0,0)。
这是目标 C 中的正确答案:
- (NSArray *)pointsTangentToCircleWithCenter:(CGPoint)centerPoint
radius:(CGFloat)radius
outerPoint:(CGPoint)outerPoint {
float dx = centerPoint.x - outerPoint.x;
float dy = centerPoint.y - outerPoint.y;
float dd = sqrt(dx*dx + dy*dy);
float a = asinf(radius / dd);
float b = atan2f(dy, dx);
float t1 = b - a;
CGPoint tangentPoint1 = CGPointMake(centerPoint.x + radius*sinf(t1),
centerPoint.y + radius*-cosf(t1));
float t2 = b + a;
CGPoint tangentPoint2 = CGPointMake(centerPoint.x + radius*-sinf(t2),
centerPoint.y + radius*cosf(t2));
NSArray *points = @[
[NSValue valueWithCGPoint:tangentPoint1],
[NSValue valueWithCGPoint:tangentPoint2]
];
return points;
}
如果将向量DO旋转角度alpha ,则可以找到向量DX的方向 (角度 alpha 为asin(len(OX) / len(DO)),这只是斜边上半径的反正弦)
您可以简单地找到向量DX的长度,如下所示:sqrt(len(DO)*len(DO) - len(OX)*len(OX))
给定向量DX的方向和长度,您可以找到点 X的值。一种方法是标准化DX并将其乘以它的长度。
auto dist = D.Distance(O); auto side = sqrt(dist*dist - rad*rad) auto line = Vector2D(D, O); line.Rotate(asin(rad / dist)); //get the direction line.Normalize(); //set length to 1 line*=side; //we have the direction, now get length Point2D X = D + line;
PS 请注意,还有第二个切线,它是通过将DO旋转负alpha找到的
对我来说,这是作业并不明显,但我确实喜欢定义直角三角形的直觉。即便如此,这个解决方案还是会有一些代数。
另一种似乎可行的方法是将问题简单地定义为两个方程在两个未知数中的解。也就是说,圆心在 (0,0) 处,半径为 R 的方程为
x^2 + y^2 = R^2
通过点 (xt,yt) 且斜率为(未知)S 的直线方程为
(y - yt) = S*(x - xt)
求解交点的两个方程组。根据 S 的值,这对方程将有零个、一个或两个解。事实证明,存在两个 S 值,因此解决方案是唯一的。求解使解唯一的 S 的这两个值,然后恢复交点 (xt,yt)。如果这是家庭作业,我不会深入讨论实际的解决方案,但这部分是微不足道的代数。
我的观点是,这种代数方法是查看计算几何问题解决方案的另一种方式。它突出了一个有趣的点,即有两条线在切点处与圆相交,并且当一条线在切点处相交时,只有一个交点。
这种方法的一个缺陷是由于某些问题的奇异性而失败。即,当斜率为 S 的直线垂直时,则 S 未定义。其他依赖简单距离和勾股定理的方法对该事件是稳健的。
使用相交方程的 x,y 坐标(圆的一个和直线的一个)。这才是重点。
如果你只有一个端点来画线,你会得到两个不同的点,因为会有两条不同的切线,一条向上,一条向下。
这是一个 C# 解决方案(它可以很容易地适应其他语言),它不使用三角函数,只使用几何构造。
你可以在这里看到一个活生生的表示(P、C 和半径可以移动):https ://www.desmos.com/calculator/ifzt2nzyl9
public static double[] GetTangentPoints(
double px,
double py,
double cx,
double cy,
double radius)
{
var points = new double[4];
var dx = cx - px;
var dy = cy - py;
if (dx == 0 && dy == 0)
return null; // no solution
// PC is distance between P and C, pc2 is PC^2
var pc2 = dx * dx + dy * dy;
var pc = Math.Sqrt(pc2);
if (pc < radius)
return null; // no solution
// R is radius of circle centered in P, r2 is R^2
var r2 = pc2 - radius * radius;
// d is the P => X0 distance (demonstration is here https://mathworld.wolfram.com/Circle-CircleIntersection.html where PC is named 'd' in there)
var d = r2 / pc;
// h is the X0 => X1 (and X0 => X2) distance
var h = Math.Sqrt(r2 - d * d);
// first tangent point
points[0] = px + (dx * d - dy * h) / pc;
points[1] = py + (dy * d + dx * h) / pc;
// second tangent point
points[2] = px + (dx * d + dy * h) / pc;
points[3] = py + (dy * d - dx * h) / pc;
return points;
}
我通常使用 Maple 软件来解决此类问题。它甚至可以从这些方程式生成 C 代码。
这是输出:
t1 = v_x * v_x;
t2 = t1 * t1;
t3 = v_y * v_y;
t6 = sqrt(t1 * t3 - t1 + t2);
t7 = v_y + t6;
t9 = 0.1e1 / (t1 + t3);
t13 = 0.1e1 / v_x;
x1 = -(t7 * t9 * v_y - 0.1e1) * t13;
y1 = t7 * t9;
t16 = (-v_y + t6) * t9;
x2 = -(-t16 * v_y - 0.1e1) * t13;
y2 = -t16;
显然,您需要将浮点数或双精度数添加到变量中,还要在取平方根之前检查负值。
另一种解决方案;不如 dmindreader 优雅,但可能更容易理解:
你知道点T
在圆上并且线OT
垂直于线PT
这给了你
abs(O - T) = R
dotProduct(O - T, P - T) = 0