Соединить две окружности. В чём ошибка?

155
22 марта 2019, 10:00

Пытаюсь повторить полигон, тот что изображен зеленой линией на рисунке ниже. Пользовался этими советами:

Совет первый Совет второй

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

Jsfiddle проект

Писал на JS, но подойдёт любое другое решение. Даже без кода, в чистой теории, в чем неверен мой подход.

//
Answer 1

Поскольку нужны только внешние касательные (могут быть ещё внутренние), то подход может быть не слишком сложным:

Пусть центр большей окружности CR, меньшей cr. Вектор разности d, его длина и нормализованный вектор:

 d = cr - CR
 dlen = length(d)
 ud = d / dlen

Общие касательные к окружностям разного радиуса пересекаются где-то в точке OP. Касательная вместе с радиусами к точкам касания образует два подобных прямоугольных треугольника (поскольку радиус перпендикулярен касательной). Из подобия следует

Coeff = R / (R - r)
OP = CR + d * Coeff

Синус и косинус угла A этого треугольника

ca = R / (dlen * Coeff)
sa = Sqrt(1-ca*ca)

Точки касания большой окружности могут быть получена поворотом вектора ud*R на A и -A

P.x = CR.x + ca * R * ud.x + sa * R * ud.y
P.y = CR.y - sa * R * ud.x + ca * R * ud.y
Q.x = CR.x + ca * R * ud.x - sa * R * ud.y
Q.y = CR.y + sa * R * ud.x + ca * R * ud.y

Аналогично для малой окружности с использованием её центра и радиуса.

Тест:

fiddle: (подправил последовательность точек и знаки углов)

let c1 = this.state.circles[0];
let c2 = this.state.circles[1];
let l = getVectorLen(c1,c2);
let v = {x: c2.x-c1.x, y: c2.y-c1.y};
let uv = {x: v.x / l, y: v.y / l};
let ca = (c2.r - c1.r) / l;
let sa = Math.sqrt(1 - ca*ca);
let ps = [
    c1.x - ca * c1.r * uv.x - sa * c1.r * uv.y, 
    c1.y + sa * c1.r * uv.x - ca * c1.r * uv.y,
    c1.x - ca * c1.r * uv.x + sa * c1.r * uv.y,
    c1.y - sa * c1.r * uv.x - ca * c1.r * uv.y,
    c2.x - ca * c2.r * uv.x + sa * c2.r * uv.y,
    c2.y - sa * c2.r * uv.x - ca * c2.r * uv.y, 
    c2.x - ca * c2.r * uv.x - sa * c2.r * uv.y, 
    c2.y + sa * c2.r * uv.x - ca * c2.r * uv.y 
];
Answer 2

На картинке 2 из вашего вопроса имеется две окружности с центрами в точках c1 и c2 радиусов r1 и r2. Центры окружностей соединены прямой. Необходимо в каждой окружности провести 2 диаметра перпендикулярных отрезку, соединяющему центры. Точки пересечений диаметров с окружностью образуют искомый четырехугольник (трапецию).

circles: [
        { r: 20, x: 50, y: 150, f: 'black'},   // c1
        { r: 50, x: 150, y: 100, f: 'black'}   // c2
    ]

Для решения необходимо определить угол alpha между вектором c1c2 и осью Х. После чего будет понятно, под каким углом проходят диаметры. Нам потребуется поставить 4 точки, каждая из которых будет удалена от центра на расстояние радиуса под углом alpha + 90 или alpha - 90.

Как известно, тангенс угла в прямоугольном треугольнике равен отношению длины противолежащего катета к прилежащему. Так что угол alpha достаточно просто вычисляется с помощью формулы (не рассматриваем ситуацию когда x1=x2):

let alpha = Math.atan( (c2.y - c1.y) / (c2.x-c1.x) );

Если нам известна исходная точка (x0, y0), и нам надо сдвинуться на расстояние R под углом a, то координаты новой точки будут иметь вид x1 = x0 + R*Cos(a) и y1 = y0 + R*Sin(a).
В данном случае R будет принимать значения r1 и r2 (радиусов окружностей), а угол с поворотом на 90 градусов - alpha + PI/2 и alpha - PI/2.

В исходном виде формула для вычисления координат первой точки будет иметь вид

x1 = c1.x + Math.cos(alpha + Math.PI/2)*c1.r;
y1 = c1.y + Math.sin(alpha + Math.PI/2)*c1.r;

Для второй - то же самое с углом alpha - Math.PI/2. Затем аналогичные равенства для окружности c2.

Вспомнив тригонометрические формулы приведения (про углы α ± π/2 и ) все эти вычисления сводятся к следующему:

let cosA = Math.cos(alpha);
let sinA = Math.sin(alpha);
let ps = [
    c1.x - sinA*c1.r, // x1 
    c1.y + cosA*c1.r, // y1    
    c1.x + sinA*c1.r, // x2 
    c1.y - cosA*c1.r, // y2
    c2.x + sinA*c2.r, // x3 
    c2.y - cosA*c2.r, // y3
    c2.x - sinA*c2.r, // x4 
    c2.y + cosA*c2.r, // y4        
];

jsfidlle

READ ALSO
Разрешить вводить только цифры/буквы в input с мобильного

Разрешить вводить только цифры/буквы в input с мобильного

У меня есть 2 input: ФИО и номер телефонаНужно, чтобы клиент в поле ФИО мог ввести только буквы, а в поле телефон - только цифры

189
JavaScript как получить элемент под элементом Drag N Drop

JavaScript как получить элемент под элементом Drag N Drop

Не могу придумать как получить элемент над которым произойдет действие onmouseup (то есть когда отпускаю перемещаемый элемент)

169
Получение сырых данных Audio в js

Получение сырых данных Audio в js

Допустим есть некий sampemp3

168