Перейти до основного вмісту

Перетин кола і прямої

Задано координати центра кола та його радіус, а також рівняння прямої. Потрібно знайти точки їх перетину.

Коли підходить цей алгоритм?
  • Одна фігура — коло (центр і радіус), а друга — пряма у вигляді Ax+By+C=0Ax+By+C=0?
  • Друга фігура теж коло, а не пряма? (якщо так → Перетин двох кіл)
  • Допустима арифметика з рухомою комою (квадратні корені), а не лише цілочислові обчислення?

Розв'язок

Замість того щоб розв'язувати систему з двох рівнянь, ми підійдемо до задачі геометрично. У такий спосіб ми отримаємо точніший розв'язок з погляду чисельної стійкості.

Без втрати загальності вважаємо, що коло має центр у початку координат. Якщо це не так, ми переносимо його туди й коригуємо сталу CC у рівнянні прямої. Отже, маємо коло з центром у точці (0,0)(0,0) радіуса rr і пряму з рівнянням Ax+By+C=0Ax+By+C=0.

Почнемо з того, що знайдемо точку на прямій, найближчу до початку координат, (x0,y0)(x_0, y_0). По-перше, вона має лежати на відстані

d0=CA2+B2d_0 = \frac{|C|}{\sqrt{A^2+B^2}}

По-друге, оскільки вектор (A,B)(A, B) перпендикулярний до прямої, координати точки мають бути пропорційними координатам цього вектора. Оскільки ми знаємо відстань точки до початку координат, нам достатньо відмасштабувати вектор (A,B)(A, B) до цієї довжини, і ми отримаємо:

x0=ACA2+B2y0=BCA2+B2\begin{align} x_0 &= - \frac{AC}{A^2 + B^2} \\ y_0 &= - \frac{BC}{A^2 + B^2} \end{align}

Знаки мінус не очевидні, але їх легко перевірити, підставивши x0x_0 та y0y_0 у рівняння прямої.

На цьому етапі ми можемо визначити кількість точок перетину і навіть знайти розв'язок, коли таких точок одна чи жодної. Справді, якщо відстань від (x0,y0)(x_0, y_0) до початку координат d0d_0 більша за радіус rr, відповідь — нуль точок. Якщо d0=rd_0=r, відповідь — одна точка (x0,y0)(x_0, y_0). Якщо ж d0<rd_0<r, то точок перетину дві, і тепер нам треба знайти їхні координати.

Отже, ми знаємо, що точка (x0,y0)(x_0, y_0) лежить усередині кола. Дві точки перетину, (ax,ay)(a_x, a_y) та (bx,by)(b_x, b_y), мають належати прямій Ax+By+C=0Ax+By+C=0 і лежати на однаковій відстані dd від (x0,y0)(x_0, y_0), а цю відстань легко знайти:

d=r2C2A2+B2d = \sqrt{r^2 - \frac{C^2}{A^2 + B^2}}

Зауважимо, що вектор (B,A)(-B, A) колінеарний прямій, а отже, ми можемо знайти шукані точки, додаючи й віднімаючи до точки (x0,y0)(x_0, y_0) вектор (B,A)(-B,A), відмасштабований до довжини dd.

Нарешті, рівняння двох точок перетину такі:

m=d2A2+B2ax=x0+Bm,ay=y0Ambx=x0Bm,by=y0+Am\begin{align} m &= \sqrt{\frac{d^2}{A^2 + B^2}} \\ a_x &= x_0 + B \cdot m, a_y = y_0 - A \cdot m \\ b_x &= x_0 - B \cdot m, b_y = y_0 + A \cdot m \end{align}

Якби ми розв'язували початкову систему рівнянь алгебраїчними методами, то найімовірніше отримали б відповідь в іншому вигляді й з більшою похибкою. Описаний тут геометричний метод наочніший і точніший.

Реалізація

Як зазначено на початку, ми вважаємо, що коло має центр у початку координат, а тому на вхід програмі подаються радіус rr кола та параметри AA, BB і CC рівняння прямої.

double r, a, b, c; // подаються на вхід
double x0 = -a*c/(a*a+b*b), y0 = -b*c/(a*a+b*b);
if (c*c > r*r*(a*a+b*b)+EPS)
puts ("no points");
else if (abs (c*c - r*r*(a*a+b*b)) < EPS) {
puts ("1 point");
cout << x0 << ' ' << y0 << '\n';
}
else {
double d = r*r - c*c/(a*a+b*b);
double mult = sqrt (d / (a*a+b*b));
double ax, ay, bx, by;
ax = x0 + b * mult;
bx = x0 - b * mult;
ay = y0 - a * mult;
by = y0 + a * mult;
puts ("2 points");
cout << ax << ' ' << ay << '\n' << bx << ' ' << by << '\n';
}

Задачі для практики

Відеоматеріали