Координаты точки эллипса по углу

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

Калькулятор точки на эллипсе

Давайте посмотрим, как это выглядит на практике. Потом теория. Оранжевый маркер отвечает за угол, на основании которого считаем координаты. Красный — параметрический угол, о котором ниже.

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

Get a better browser, bro…

Параметрическое уравнение эллипса

Обратимся, как обычно, к Википедии. Находим там следующее:

Каноническое уравнение эллипса может быть параметризовано:

(1) Параметрическое уравнение эллипса

Очевидно, что t — это угол, и это не «наш» угол. Это какой-то другой угол, который функционально связан с «нашим». «Нашим» называю угол, от которого требуется посчитать координаты.

Таким образом, задача нахождения координат точки эллипса по углу сводится к задаче нахождения угла t, зависящим от требуемого. Нахождением этой зависимости и займемся.

Подготовка

Рис.1. Построение эллипса

У нас есть эллипс, описанный двумя полуосями a и b. Представим две окружности, имеющих общий центр. Меньшая окружность (зеленая) имеет радиус b. Большая окружность (синяя) имеет радиус a.

Проведем прямую из общего центра [X0;Y0] в произвольную точку плоскости [X;Y]. В результате пересечения с этими окружностями получаются две точки [X1;Y1]  и [X2;Y2].

α – угол между прямой и осью X.

Малая окружностьX1 = b × cos αY1 = b × sin α
Большая окружностьX2 = a × cos αY2 = a × sin α
Таблица 1. Координаты точек пересечения прямой с окружностями

Нахождение зависимости

Рис.2. Угол β для точки эллипса [X’;Y’]

Используя уравнение (1) посчитаем координаты точки на эллипсе [X’;Y’] для угла α. Проведем прямую из центра [X0;Y0] в точку [X’;Y’]. Угол β – угол между этой прямой и осью X.

Задача сводится к тому, чтобы найти такой α, при котором β был бы равен интересующему нас углу. Таким образом, угол α будет являться параметром в уравнении (1) для требуемого угла β.

Найдем зависимость между получившимся углом β и углом α. На рисунке видно, что прилегающий к углу катет (синий) равен ранее рассчитанному X2, а противолежащий (зеленый) равен Y1:

X’ = X2 = a × cos α

Y’ = Y1 = b × sin α

Опыт показывает, что тут зачастую возникает легкий ступор. Возможно, рисунок вводит в некое заблуждение. Видим треугольник, и если с синим катетом вопросов нет, то с зеленым — масса. Почему синус от α? Угол «вона где», тут синус вообще не от того угла и т.д.

Смотрим на пересечение прямой и малой (зеленой) окружности. Зеленый катет прилетает именно оттуда. Именно так координату Y’ и рассчитывали, согласно уравнению(1). Рисунок — это иллюстрация, не метод решения.

Тангенс угла β в этом случае равен:

Latex formula

(3) Тангенс угла β

Используя формулу тангенса произведем дальнейшие преобразования:

Latex formula

Latex formula

Latex formula

(4) Зависимость тангенса α от тангенса β

Таким образом, видим прямую зависимость угла α, который нужен нам в качестве параметра в уравнении(1), от угла β, координаты точки от которого хотим получить.

Нахождение координат

Угол α находим через арктангенс. В Delphi (и не только) для этих целей используется функция ArcTan2 из модуля math. Она корректно возвращает знак ± угла в зависимости от квадранта, а также предусмотрительно нечувствительна к возможным коллизиям, типа деления на 0.

Находим синус и косинус от требуемого угла β и подставляем в параметры функции ArcTan2, согласно последней формуле (4):

//-- находим параметр (некий угол) для уравнения ---
 SinCos(Angle,sn,cs); 
 t := ArcTan2(a*sn, b*cs);

Получившийся в результате вызова ArcTan2 угол есть ничто иное, как параметр t в параметрическом уравнении (1). Подставив его в уравнение, находим координаты точки на эллипсе, отстоящей на заданный угол от оси X.

О параметре

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

В JavaScript’е нет понятия эллипс. Тем более нет понятия дуги эллипса. Но можно нарисовать окружность (через дугу) и «сплющить». Может быть такой номер пройдет и с дугой?

// рисует дугу эллипса
function drawArcEllipse(ctx, center, a, b, start, finish, 
  colorLine='none', widthLine=0.0, angle=0.0) {
    if (a==0.0) return;
    var t1 = start;
    var t2 = finish;
    ctx.beginPath();

    // сохраняем контекст
    ctx.save(); 
    // перемещение координат в центр эллипса
    ctx.translate(center.x, center.y); 
    // поворот плоскости на угол, если требуется 
    if (angle!=0.0) ctx.rotate(angle); 
    // сжимаем по вертикали
    ctx.scale(1, b/a); 
    // рисуем дугу
    ctx.arc(0, 0, a, t1, t2); 
    // восстанавливает контекст
    ctx.restore(); 

    if (colorLine!='none')
        ctx.strokeStyle = colorLine;
    if (widthLine>0.0)
        ctx.lineWidth = widthLine;
    ctx.stroke(); 
    ctx.closePath();
}

Рис.3. Использование параметра эллипса

На рисунке слева видим, что дуга расположена совершенно неправильно. Очевидно, что надо использовать какие-то другие углы. Вот тут на помощь приходит параметр эллипса. Это как раз тот самый угол, который обеспечивает «попадание» в нужный нам угол при «сплющивании» окружности.

Перепишем функцию с учетом нахождения параметра:

// рисует дугу эллипса
function drawArcEllipse(ctx, center, a, b, start, finish, colorLine='none', widthLine=0.0, angle=0.0) {
    if (a==0.0) return;
    
    var sn = Math.sin(start);
    var cs = Math.cos(start);
    var t1 = Math.atan2(a*sn, b*cs);

    sn = Math.sin(finish);
    cs = Math.cos(finish);
    var t2 = Math.atan2(a*sn, b*cs);

    ctx.beginPath();

    // сохраняем контекст
    ctx.save(); 
    // перемещение координат в центр эллипса
    ctx.translate(center.x, center.y); 
    // поворот плоскости на угол, если требуется 
    if (angle!=0.0) ctx.rotate(angle); 
    // сжимаем по вертикали
    ctx.scale(1, b/a); 
    // рисуем дугу
    ctx.arc(0, 0, a, t1, t2); 
    // восстанавливает контекст
    ctx.restore(); 

    if (colorLine!='none')
        ctx.strokeStyle = colorLine;
    if (widthLine>0.0)
        ctx.lineWidth = widthLine;
    ctx.stroke(); 
    ctx.closePath();
}

На рисунке справа видим, что все встало на свои места. Идеальная дуга )

Практика

Две функции. Первая находит параметр t по углу. Вторая производит расчет координат. Из второй не вызываю первую, т.к. получится двойное вычисление полуосей. Код не настолько велик, чтобы его нельзя было продублировать.

//******************************************************************
//   Найти угол, который будет использован в расчете точки на элипсе
//   Т.е. тот самый параметр t в параметрическом уравнении эллипса:
//     x = a * cos t
//     y = b * sin t 
//******************************************************************
function GetEllipseAngleParam(ARect : TRectF;
   Angle : Extended) : Extended;
 var sn,cs : Extended; // синус/косинус
     a,b : Extended;   // полуоси по X/Y
 begin
   a := ARect.Width/2;
   b := ARect.Height/2;
   SinCos(Angle,sn,cs);
   result := ArcTan2(a * sn, b * cs);
 end;
//********************************************************************
//   Найти координату точки на эллипсе по углу отклонения
//********************************************************************
function CalcEllipsePointCoord(ARect : TRectF;
   Angle : extended) : TPointF;
 var sn,cs : Extended; // синус/косинус
     a,b : Extended;   // полуоси по X/Y
     cnt : TPointF;    // центр
     t   : Extended;   // параметр для уравнения эллипса
 begin
   // инициализация полуосей 
   a := ARect.Width/2;
   b := ARect.Height/2;
   // центр эллипса 
   cnt := ARect.CenterPoint;
   // находим параметр (некий угол) для уравнения 
   SinCos(Angle,sn,cs);
   t := ArcTan2(a * sn, b * cs);
   // считаем результат по параметрическому уравнению 
   SinCos (t, sn, cs);
   result.X := cnt.x + a * cs;
   result.Y := cnt.Y + b * sn;
 end;

Скачать исходник + исполнямый файл


Друзья, спасибо за внимание!

Надеюсь, материал после правок стал понятней.

Подписывайтесь на телегу.

Если есть вопросы, с удовольствием отвечу )


5 4 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x