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

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

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

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

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

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

4 ответов


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

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

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))

это можно переписать (с помощью формул идентификаторов " продукт-сумма) as:

(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))

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


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

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

вот моя реализация JavaScript этих уравнений, взятых из интерактивная демонстрация эллиптических дуговых путей, используя Сильвестр.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);

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

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

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


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

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

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