Как рассчитать центр эллипса по двум точкам и размерам радиуса - PullRequest
19 голосов
/ 13 октября 2008

Работая над реализацией SVG для Internet Explorer на основе собственного формата VML, я столкнулся с проблемой перевода эллиптической дуги SVG в эллиптическую дугу VML.

В VML дуга задается как: два угла для двух точек на эллипсе и длины радиусов, В SVG дуга задается: двумя парами координат для двух точек на эллипсе и размерами граничной рамки эллипса

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

Обновление : Давайте выполним предварительное условие, согласно которому эллипс обычно размещается (его радиусы параллельны оси линейной системы координат), поэтому вращение не применяется.

Обновление : Этот вопрос относится не к элементу svg: ellipse, а к команде "a" эллиптической дуги в элементе svg: path ( Пути SVG: команды эллиптической дуги )

Ответы [ 4 ]

23 голосов
/ 13 октября 2008

Итак, решение здесь:

Параметризованная формула эллипса:

x = x0 + a * cos(t)
y = y0 + b * sin(t)

Давайте добавим к нему известные координаты двух точек:

x1 = x0 + a * cos(t1)
x2 = x0 + a * cos(t2)
y1 = y0 + b * sin(t1)
y2 = y0 + b * sin(t2)

Теперь у нас есть система уравнений с 4 переменными: центр эллипса (x0 / y0) и двумя углами t1, t2

Давайте вычтем уравнения, чтобы избавиться от координат центра:

x1 - x2 = a * (cos(t1) - cos(t2))
y1 - y2 = b * (sin(t1) - sin(t2))

Это может быть переписано (с формулами тождеств продукта к сумме) как:

(x1 - x2) / (2 * a) = sin((t1 + t2) / 2) * sin((t1 - t2) / 2)
(y2 - y1) / (2 * b) = cos((t1 + t2) / 2) * sin((t1 - t2) / 2)

Давайте заменим некоторые уравнения:

r1: (x1 - x2) / (2 * a)
r2: (y2 - y1) / (2 * b)
a1: (t1 + t2) / 2
a2: (t1 - t2) / 2

Тогда мы получим простую систему уравнений:

r1 = sin(a1) * sin(a2)
r2 = cos(a1) * sin(a2)

Деление первого уравнения на второе дает:

a1 = arctan(r1/r2)

Добавление этого результата к первому уравнению дает:

a2 = arcsin(r2 / cos(arctan(r1/r2)))

Или просто (с использованием композиций функций триггера и обратной функции):

a2 = arcsin(r2 / (1 / sqrt(1 + (r1/r2)^2)))

или даже более просто:

a2 = arcsin(sqrt(r1^2 + r2^2))

Теперь исходная система четырех уравнений может быть легко решена, и можно найти все углы, а также координаты центра затмения.

5 голосов
/ 13 июля 2012

Отправленная вами ссылка на дугу эллиптической кривой включает в себя ссылку на примечания по реализации эллиптической дуги .

Здесь вы найдете уравнения для преобразования из конечной точки в центральную параметризацию .

Вот моя реализация JavaScript этих уравнений, взятая из интерактивной демонстрации путей эллиптической дуги , использующей Sylvester.js для выполнения матричных и векторных вычислений.

// Calculate the centre of the ellipse
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
var x1 = 150;  // Starting x-point of the arc
var y1 = 150;  // Starting y-point of the arc
var x2 = 400;  // End x-point of the arc
var y2 = 300;  // End y-point of the arc
var fA = 1;    // Large arc flag
var fS = 1;    // Sweep flag
var rx = 100;  // Horizontal radius of ellipse
var ry =  50;  // Vertical radius of ellipse
var phi = 0;   // Angle between co-ord system and ellipse x-axes

var Cx, Cy;

// Step 1: Compute (x1′, y1′)
var M = $M([
               [ Math.cos(phi), Math.sin(phi)],
               [-Math.sin(phi), Math.cos(phi)]
            ]);
var V = $V( [ (x1-x2)/2, (y1-y2)/2 ] );
var P = M.multiply(V);

var x1p = P.e(1);  // x1 prime
var y1p = P.e(2);  // y1 prime


// Ensure radii are large enough
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
// Step (a): Ensure radii are non-zero
// Step (b): Ensure radii are positive
rx = Math.abs(rx);
ry = Math.abs(ry);
// Step (c): Ensure radii are large enough
var lambda = ( (x1p * x1p) / (rx * rx) ) + ( (y1p * y1p) / (ry * ry) );
if(lambda > 1)
{
    rx = Math.sqrt(lambda) * rx;
    ry = Math.sqrt(lambda) * ry;
}


// Step 2: Compute (cx′, cy′)
var sign = (fA == fS)? -1 : 1;
// Bit of a hack, as presumably rounding errors were making his negative inside the square root!
if((( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) )) < 1e-7)
    var co = 0;
else
    var co = sign * Math.sqrt( ( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) ) );
var V = $V( [rx*y1p/ry, -ry*x1p/rx] );
var Cp = V.multiply(co);

// Step 3: Compute (cx, cy) from (cx′, cy′)
var M = $M([
               [ Math.cos(phi), -Math.sin(phi)],
               [ Math.sin(phi),  Math.cos(phi)]
            ]);
var V = $V( [ (x1+x2)/2, (y1+y2)/2 ] );
var C = M.multiply(Cp).add(V);

Cx = C.e(1);
Cy = C.e(2);
1 голос
/ 13 октября 2008

Промежуточный вопрос довольно прост ... вы не делаете. Вы определяете центр эллипса из ограничивающего прямоугольника (а именно центр прямоугольника является центром эллипса, если эллипс центрирован в прямоугольнике).

Для вашего первого вопроса я бы посмотрел на полярную форму уравнения эллипса, которая доступна в Википедии . Тебе также нужно определить эксцентриситет эллипса.

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

1 голос
/ 13 октября 2008

Эллипс не может быть определен только двумя точками. Даже круг (специальный эллипс в обкладке) определяется тремя точками.

Даже с тремя точками у вас будут бесконечные эллипсы, проходящие через эти три точки (подумайте: вращение).

Обратите внимание, что ограничивающий прямоугольник указывает центр эллипса и, скорее всего, предполагает, что его большая и вспомогательная оси параллельны осям x, y (или y, x).

...