Найти касательную точку на окружности? - PullRequest
11 голосов
/ 29 августа 2009

Учитывая линию с первой конечной точкой P (x1, y1), другая конечная точка неизвестна, пересекается с окружностью, которая находится в начале координат с радиусом R только в одной точке (касательной) T (x2, y2). Кто-нибудь знает, как получить точку Т? Заранее спасибо!

Ответы [ 9 ]

35 голосов
/ 29 августа 2009

Учитывая линию с первой конечной точкой P (x1, y1), другая конечная точка неизвестна, пересекается с окружностью, которая находится в начале координат с радиусом R только в одной точке (касательной) T (x2, y2). Кто-нибудь знает, как получить точку Т?

Некоторые другие решения кажутся немного излишними. Я думаю, что самый простой способ - просто заметить, что это прямоугольный треугольник с вершинами P, T и O (начало координат). Угол ВОМ - это прямой угол, потому что касательная линия всегда находится под прямым углом к ​​радиусу.

Вы знаете длину TO, потому что она имеет длину r и имеет вершину в начале координат; Вы знаете OP, потому что знаете, где O и P. Учитывая две стороны прямоугольного треугольника, легко найти длину и направление третьей стороны. Это домашнее задание, поэтому остальное я оставлю читателю в качестве упражнения.

                    __...------__    T(x2, y2)                      
               _.-''             -(+)
            ,-'                   |----             
          ,'                     |     ----
        ,'                      |       '  ----
       /                       |         `     ----       
      /                       |           `.       ----   
     /                       |             \           ----
    |                       |               |              ----
    |                      |                 |                  ----
    |                     |                  |                      ----
    |                   (+)---------------------------------------------(+) P (x1,y1)
    |                                        .'        
    |                    O                   |         
     |                                      .'         
      \                                     /          
       \                                  ,'           
        `                                /             
         '.                            ,'              
           '-.                      _,'             
              '-._              _,(+)  T'(x3, y3)                   
                  '`--......---'                       

Есть два возможных направления для TO, поскольку точка T 'также является действительной точкой касания, поэтому у вас будет два конгруэнтных треугольника.

14 голосов
/ 06 апреля 2013

Все, что вам нужно, это ответ 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) };
9 голосов
/ 29 августа 2009

Возьмите 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)

это все, что вам нужно.

5 голосов
/ 14 августа 2015

В ответе 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;
}
3 голосов
/ 05 ноября 2013
  1. Вы можете найти направление вектора DX , если повернуть вектор DO на угол альфа (угол альфа определяется как asin (len (OX) / len (DO)) , то есть просто арксинус радиуса над гипотенузой)

  2. Вы можете найти длину вектора DX тривиально следующим образом: sqrt (len (DO) * len (DO) - len (OX) * Len (ОХ))

  3. Учитывая направление и длину вектора DX , вы можете найти значение point 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

Image demonstrating the algo

2 голосов
/ 30 августа 2009

Для меня не очевидно, что это домашняя работа, но мне нравится интуиция, что определяется прямоугольный треугольник. Несмотря на это, будет некоторая алгебра с этим решением.

Другой подход, который кажется жизнеспособным, состоит в том, чтобы просто определить проблему как решение двух уравнений с двумя неизвестными. То есть уравнение окружности с центром в (0,0) с радиусом R равно

x^2 + y^2 = R^2

Уравнение прямой, проходящей через точку (xt, yt) с (неизвестным) наклоном S, равно

(y - yt) = S*(x - xt)

Решить систему двух уравнений для точки пересечения. В зависимости от значения S, у этой пары уравнений будет ноль, одно или два решения. Также окажется, что есть два значения S, так что решение является уникальным. Решите для тех двух значений S, которые делают решение уникальным, затем восстановите точку пересечения (xt, yt). Я не буду вдаваться в подробности фактического решения, если это домашнее задание, но эта часть - тривиальная алгебра.

Моя точка зрения заключается в том, что этот алгебраический подход является еще одним способом посмотреть на решение проблемы вычислительной геометрии. Это подчеркивает интересный момент в том, что есть две линии, которые пересекают окружность в точке касания, и что, когда линия пересекается в точке касания, существует единственная точка пересечения.

Недостаток этого подхода состоит в том, что он терпит неудачу из-за особенности НЕКОТОРЫХ проблем. То есть, когда линия с наклоном S является вертикальной, то S не определена. Другие подходы, основанные на простых расстояниях и теореме Пифагора, устойчивы к этому событию.

1 голос
/ 10 октября 2016

Я обычно использую программное обеспечение Maple для решения таких проблем. Он может даже генерировать C-код из этих уравнений.

enter image description here

Вот вывод:

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 голос
/ 29 августа 2009

Используйте координаты x, y пересекающихся уравнений (один из круга и один из линии). В этом все дело.

Если у вас есть только одна конечная точка, из которой можно нарисовать линию, вы получите две разные точки, поскольку будут две разные касательные линии, одна вверх и одна вниз.

0 голосов
/ 29 августа 2009

Другое решение; менее элегантный, чем dmindreader`s, но, возможно, проще для понимания:

Вы знаете, что точка T находится на окружности и что линия OT перпендикулярна линии PT

, что дает вам

abs(O - T) = R
dotProduct(O - T, P - T) = 0
...