在圆上找到一个切点?

16

给定一条以第一个端点P(x1,y1)为起点的线段,另一个端点未知,与位于原点、半径为R的圆在唯一一点(切点)T(x2,y2)相交。有人知道如何获得点T吗?提前致谢!


如果只给出一个端点,那么它怎么能成为一条线呢? - obelix
他确实有两个不同的端点,因为他知道这条线是切线。 - andandandand
1
dmindreader 你真的理解我的意思。谢谢。 - HanWu
8
这是与数学相关的内容,而非编程。 - Argalatyr
11个回答

41
给定一条线段,其第一个端点为P(x1,y1),另一个端点T(x2,y2)在圆心为原点、半径为R的圆上与该线段相切。有人知道如何获得点T吗?
其他一些解决方案似乎有点过度复杂。我认为最简单的方法就是注意到这是一个直角三角形,其顶点为P、T和O(原点)。PTO的角度是直角,因为切线总是与半径成直角。
你知道TO的长度,因为它的长度是r并且具有原点的顶点;你知道OP,因为你知道O和P的位置。已知直角三角形的两边,很容易找到第三边的长度和方向。这是作业,所以我把剩下的留给读者练习。
                    __...------__    T(x2, y2)                      
               _.-''             -(+)
            ,-'                   |----             
          ,'                     |     ----
        ,'                      |       '  ----
       /                       |         `     ----       
      /                       |           `.       ----   
     /                       |             \           ----
    |                       |               |              ----
    |                      |                 |                  ----
    |                     |                  |                      ----
    |                   (+)---------------------------------------------(+) P (x1,y1)
    |                                        .'        
    |                    O                   |         
     |                                      .'         
      \                                     /          
       \                                  ,'           
        `                                /             
         '.                            ,'              
           '-.                      _,'             
              '-._              _,(+)  T'(x3, y3)                   
                  '`--......---'                       

对于TO,有两个可能的方向,因为点T'也是一个有效的切点,所以你将会得到两个相等的三角形。


2
这被称为泰勒斯定理 - jonathancardoso
@JCM 我不明白这怎么是塔勒定理。塔勒只会说明 OPOPT 的外接圆的直径。 - Ingo Bürk
这不像一个直角三角形。事实上,点T和T'将取决于点P离圆有多近,因此角度也会不同。我看不出为什么这个答案会得到那么多赞。 - Hi-Angel
1
这是正确的,因为正是切线的工作原理。切线(PT)始终垂直于连接到切点(T)的圆的半径。因此,它保证是一个直角三角形。角TOP和OPT将不同,但PTO仍将保持直角。试一试,你就会看到! - John Feminella

18

dmckee的回答包含了你需要的所有内容,但如果你想查看一些代码,请查看使用Javascript和HTML canvas实现的以下示例。

完整示例: 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) };

x:radius代表什么? - 130nk3r5
1
半径=圆的半径。 ta=一个具有x和y属性的对象(基本上是一个点)。 回答你的问题,x:radius本身并没有太多意义。该代码定义了ta.x的值,其中x = radius * Math.sin(t)。 请确保参考jsfiddle以查看工作示例。 - imbrizi
我正在尝试将此转换为C#代码...但我无法弄清楚x:radius是什么意思... 但我读错了代码...我误解它为具有值半径的对象x...实际上它只是ta的值x。 谢谢;) - 130nk3r5
不需要调用atan2函数,因为b只是pi/2-a。 - Tony
如果cx和cy都为0,则此代码有效。但是,如果圆不在原点上,则需要通过将中心坐标添加到切点来进行调整:ta = { x:radius * Math.sin(t) + cx, y:radius * -Math.cos(t) + cy};t = b + a tb = { x:radius * -Math.sin(t) + cx, y:radius * Math.cos(t) + cy}; - Richard
显示剩余2条评论

10

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)

这就是你所需要的全部。


1
@HanWu:在画图并检查后,我仍然得到了 arcsin。你应该使用 arccos 来求连接线 (CP) 和半径 (CT) 之间的夹角。 - dmckee --- ex-moderator kitten
arcsin 是正确的:正弦(alpha)= 对边(R)/ 斜边(D)。 - Argalatyr
\alpha = arcsin(R/D) 应该为 \alpha = arccos(R/D)。 - HanWu
1
我在思考这个问题,似乎是不正确的。具体来说,没有任何配置可以使某些正弦等于R/D *(也不是余弦)*。你想让D成为斜边,但是斜边只有在直角三角形中才有定义,而无论你如何构造与切点和D上某点的直角三角形,斜边总是从切点到外部点的线段,因为它总是更长的。 - Hi-Angel
这个答案有很多可疑之处...特别是长度。 - Krupip
我可能没有正确地传达信息,但数学是正确的。就John答案中的图形而言,R是线段OT的长度,D是线段OP的长度,alpha是角度OPT,L是线段TP的长度。它是正确的。 - dmckee --- ex-moderator kitten

7

imbrizi的答案假设圆心为(0,0)。

以下是Objective 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;
}

4
  1. 如果你将矢量 DO 旋转角度 alpha (角度可以通过 asin(len(OX) / len(DO)) 找到,这是半径与斜边之比的反正弦函数),就可以找到向量 DX 方向

  2. 你可以轻松地按如下方式找到向量 DX 长度sqrt(len(DO)*len(DO) - len(OX)*len(OX))

  3. 已知向量 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;

P.S. 注意,还有第二条正切线,它是通过将 DO 逆时针旋转 alpha 角度找到的。

演示算法的图片


2

我不确定这是否是一份作业,但我喜欢直角三角形的定义。即便如此,解决问题仍需要一些代数运算。

另一种看起来可行的方法是将问题简单地定义为两个未知数的两个方程的解。也就是说,以坐标为(0,0),半径为R的圆的方程为

x^2 + y^2 = R^2

通过点(xt,yt)且斜率未知的直线方程为:
(y - yt) = S*(x - xt)

解决两个方程组的交点。根据S的值,这对方程可能有零、一个或两个解。还将证明有两个S的值使得解是唯一的。解出使解唯一的这两个S的值,然后恢复交点(xt,yt)。如果这是作业,我不会深入讲解实际解决方案,但这部分是微不足道的代数。
我的观点是,这种代数方法是解决计算几何问题的另一种方式。它突显了一个有趣的观点,即有两条直线在切点处与圆相交,当一条直线在切点处相交时,存在一个唯一的交点。
这种方法的缺陷是,在某些问题中由于奇异性而失败。也就是说,当斜率为S的线是垂直的时,S是未定义的。依赖于简单距离和勾股定理的其他方法对该事件具有鲁棒性。

1

我通常使用Maple软件来解决这类问题,它甚至可以从这些方程式生成C代码。

enter image description here

Here's the output:

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;

显然,您需要将变量添加为float或double类型,在进行平方根操作之前还需检查负值。

1
使用相交方程的x,y坐标(圆和线的方程之一)。那就是这个点。
如果您只有一个端点来绘制线,则会得到两个不同的点,因为将有两条不同的切线,一条向上,一条向下。

1
这里有一个C#解决方案(可以轻松适应其他语言),它不使用三角函数,只使用几何构造。
您可以在此处查看实时演示(可以移动P、C和半径):https://www.desmos.com/calculator/ifzt2nzyl9

enter image description here

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;
}

0

这是我的C#答案。只有7行代码。你必须观看“Youtube A Miraculous Proof (Ptolemy's Theorem) - Numberphile”才能弄清楚它的工作原理。该代码已经测试并产生了正确的答案。抱歉插入了换行符。我只是从Visual Studio复制到这里。

    static void FindTangentPoints(float r, PointF p, out PointF tangent1, out PointF tangent2)
    {
        float incidentLength = MathF.Sqrt(p.X * p.X + p.Y * p.Y); //the distance from the origin to the point outside the circle
        
        float reflectedLength = (r * r) / incidentLength; //see Youtube A Miraculous Proof (Ptolemy's Theorem) - Numberphile           
                                                          //take particular note when Prof Stankova writes OA1 * OA = R*R            
        PointF p2 = new PointF(p.X * reflectedLength / incidentLength, p.Y * reflectedLength / incidentLength);  //the inversion in the plane of pointOutsideTheCircle

        float pRadians = MathF.Atan2(p.Y, p.X); //the same as the angle to point p2 

        float angleP2OT = MathF.Acos(MathF.Sqrt(p2.X * p2.X + p2.Y * p2.Y) / r); //the angle between the tangent point and p

        tangent1 = new PointF(r * MathF.Cos(pRadians + angleP2OT), r * MathF.Sin(pRadians + angleP2OT));
        
        tangent2 = new PointF(r * MathF.Cos(pRadians - angleP2OT), r * MathF.Sin(pRadians - angleP2OT));
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接