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

Для нахождения координат точки эллипса по углу существует простое и элегантное решение.

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

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

Википедия: Эллипс

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

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

И вот тут возникает главный вопрос — что такое параметр t, и как его найти?

Высоколобые математики свысока ухмыляются и уходят от ответа, ибо недостойна эта ерунда ответа… Ну да ладно. Пусть идут.

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

Геометрическая иллюстрация параметризации эллипса (анимация). Автор: 09glasgow09 — собственная работа, CC BY-SA 3.0, Ссылка
Рис.1. Построение эллипса

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

X = R × cos α

Y = R × sin α

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

Далее, используя уравнение (1) считаем координаты точки на эллипсе [X’;Y’], и понимаем, что точка замечательно лежит на линии эллипса, но никак не на линии, которая требуется.

Катеты, углы и тангенсы

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

На рисунке 2. β – угол между прямой проходящей из центра эллипса в рассчитанную ранее точку [X’;Y’] на эллипсе. Задача сводится к тому, чтобы найти такой α, при котором β был бы равен интересующему нас углу.

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

(2) Расчет координат точки на эллипсе

Тангенс угла β в таком случае равен (отношение противолежащего катета к прилежащему):

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

Таким образом, преобразуя (3) (справочник [2]), получаем запись:

(4) Нахождение тангенса α

Далее, через арктангенс находим угол α при  β, равным интересующему нас углу. В Delphi надо использовать функцию ArcTan2 из модуля math, т.к. она корректно возвращает знак ± угла в зависимости от квадранта, а также предусмотрительно нечувствительна к возможным коллизиям, типа деления на 0. Параметрами ArcTan2 являются значения y и x, т.е. подразумевается, что так передается отношение y/x, что является тангенсом по сути.

Уравнение (4) представлено дробью. Тем самым определены параметры для вызова функции.

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

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

Вывод результата

Рис.3. Точка на эллипсе при заданном угле

Для угла β = 46.6 находим α=t=70. После этого подставляем в уравнение (1) и находим требуемые значения для точки на эллипсе.

Почему такое странное значение 46.6? Не 45, скажем. Просто на рисунке так все удачно расположилось, все видно. В примере к статье, который качаем ниже, можно рассмотреть любые углы и ситуации. Все рисунки – это скрины интерфейса.

Практика

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

//******************************************************************
//   Найти угол, который будет использован в расчете точки на элипсе
//   Т.е. тот самый параметр t в параметрическом уравнении эллипса:
//     x = a * cos t
//     y = b * sin t 
//******************************************************************
function GetEllipseAngleParam(ARect : TxRect; 
                              Angle : Extended) : Extended;
var sn,cs : Extended; // синус/косинс
    a,b : Extended;   // полуоси по X/Y
begin
  a := xWidthRect(Arect)/2;
  b := xHeightRect(Arect)/2;
  SinCos(Angle,sn,cs);
  result := ArcTan2(a*sn, b*cs);
end;
//********************************************************************
//   Найти координату точки на эллипсе по углу отклонения
//********************************************************************
function CalcEllipsePointCoord(ARect : TxRect; 
                               Angle : extended) : TxPoint;
var sn,cs : Extended; // синус/косинс
    a,b : Extended;   // полуоси по X/Y
    cnt : TxPoint;    // центр
    t   : Extended;   // параметр для уравнения эллипса
begin
//-- инициализация полуосей ------------------------
  a := xWidthRect(Arect)/2;
  b := xHeightRect(Arect)/2;
//-- центр эллипса ---------------------------------
  cnt := CenterRect(ARect);
//-- находим параметр (некий угол) для уравнения ---
  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;

В предложение Uses надо добавить math (в секцию implementation), xIpDraw.

Выдержка из xIPDraw по используемым типам и функциям:

Type
//-- вещественная точка ------------------------------
  PxPoint = ^TxPoint;
  TxPoint = packed record
    X : single;
    Y : single;
  end;
//-- вещественный прямоугольник ----------------------
  PxRect = ^TxRect;
  TxRect = packed record
    case Integer of
      0: (Left, Top, Right, Bottom: single);
      1: (TopLeft, BottomRight: TxPoint);
  end;

//******************************************************************
// ширина и высота "вещественного" прямоугольника
//******************************************************************
function xWidthRect (ARect : TxRect) : double;
begin
  result := ARect.Right - ARect.left;
end;

function xHeightRect (ARect : TxRect) : double;
begin
  result := ARect.Bottom - ARect.Top;
end;

//******************************************************************
//  Найти центр прямоугольника  
//******************************************************************
function CenterRect (ARect : TxRect) : TxPoint;
begin
  result.x := ARect.Left + (ARect.Right - ARect.Left) / 2;
  result.y := ARect.Top  + (ARect.Bottom - ARect.Top) / 2;
end;

Пример использования

Снова извиняюсь за объемный код, но хотелось сделать максимально информативно и красиво.

Клик мышки по полю со схемой блокирует/разблокирует реакцию на курсор. Т.е. линия, пересекающая эллипс, «застывает». Слева два режима отрисовки. Галка AntiAlias снимает/устанавливает режим сглаживания линий.

Выпадающий список соответствует заголовкам теоретической части.

1. Построение эллипса
2. Катеты и углы
3. Вывод результата

Если Вы скачали и настроили GDI+ Canvas, подкаталог GDIPCanvas в примере становится неактуальным. Имеет смысл удалить его и убрать в опциях проекта SearchPath запись «GDIPCanvas\»:

Опции проекта

Давайте напишем что-нибудь полезное? Например, круговую диаграмму.

Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *