Касательные к двум окружностям



Даны две окружности
Необходимо найти две прямые, которые касаются обеих окружностей

    Очевидно, что такие прямые существуют, и их всегда две. Если окружности имеют одинаковый радиус, то нам надо просто отложить две параллельные прямые на расстоянии равном радиусу от прямой, проходящей через центры окружностей.
    Если прямые имеют разный радиус, то существует такая точка, в которой касательные к обеим окружностям будут пересекаться. С помощью подобия треугольников найдём расстояние до этой точки, а зная вектор, направленные на неё, найдём саму точку. После этого точки касания можно найти с помощью функций поворота точки вокруг найденной нами точки.


Листинг C++

double SAT (point a, point b, point c)
{
    return 0.5 * (a.x * b.y + b.x * c.y + c.x * a.y - a.y * b.x - b.y * c.x - c.y * a.x);
}

point add_vector (point a, point v, double k)
{
    return point (a.x + v.x * k, a.y + v.y * k);
}

point turn (point p, double s, double c)
{
    double x = p.x * c - p.y * s;
    double y = p.x * s + p.y * c;
    return point (x, y);
}

point turn_of (point p, double s, double c, point p0)
{
    p.x -= p0.x;
    p.y -= p0.y;
    
    point t = turn (p, s, c);
    
    t.x += p0.x;
    t.y += p0.y;
    return  t;
}

void touch_circles (circle c1, circle c2, point & p1, point & p2, point & p3, point & p4)
{
    if (eq (c1.r, c2.r))    // если окружности одинаковых радиусов
    {
        double vx = c2.c.x - c1.c.x;    // находим вектор от центра первой окружности к центру второй
        double vy = c2.c.y - c1.c.y;
        double k = sqrt (sqr (vx) + sqr (vy)); // находим расстояние между центрами
        vx /= k;    // делим векторы на расстояние и получаем единичный вектор
        vy /= k;
        
        point pv1 (- vy, vx);    // строим перпендикулярные векторы единочной длины к найденному вектору
        point pv2 (vy, - vx);
        
        p1 = add_vector (c1.c, pv1, c1.r);    // откладываем перпендикуляры и получаем точки касания
        p2 = add_vector (c2.c, pv1, c2.r);
        p3 = add_vector (c1.c, pv2, c1.r);
        p4 = add_vector (c2.c, pv2, c2.r);
        
        return ;
    }
    
    if (c1.r > c2.r)
        swap (c1, c2);
    
    double d = dist (c1.c, c2.c);
    double l2 = d * c2.r / (c2.r - c1.r);    // находим расстояние до точки, в которой пересекаются касательные
    point v ((c1.c.x - c2.c.x) / d, (c1.c.y - c2.c.y) / d);    // находим единичный вектор от второй окружности к первой
    point t = add_vector (c2.c, v, l2);    // находим точку пересечения касательных
    double l1 = l2 - d;
    
    double f1 = sqrt (sqr (l1) - sqr (c1.r));    // расстояние от центра до точек касания
    double f2 = sqrt (sqr (l2) - sqr (c2.r));    
    double s = c2.r / l2;    // синус угла
    double c = f2 / l2;    // косинус угла
    
    point t1 = turn_of (c2.c, s, c, t);
    point t2 = turn_of (c2.c, - s, c, t);
    point v1 ((t1.x - t.x) / l2, (t1.y - t.y) / l2);    // находим единичные векторы, направленные от точки пересечения касательных до точек касания
    point v2 ((t2.x - t.x) / l2, (t2.y - t.y) / l2);
    
    p1 = add_vector (t, v1, f1);
    p2 = add_vector (t, v1, f2);
    p3 = add_vector (t, v2, f1);
    p4 = add_vector (t, v2, f2);
}


14:58
04.08.2009


По всем вопросам обращаться: rumterg@gmail.com