Pregunta

Mientras trabajaba en la implementación de SVG para que Internet Explorer se basara en su propio formato VML, encontré un problema de traducción de un arco elíptico SVG a un arco elíptico VML.

En VML, un arco viene dado por: dos ángulos para dos puntos en elipse y longitudes de radios, En SVG, un arco viene dado por: dos pares de coordenadas para dos puntos en elipse y tamaños del cuadro de límite de elipse

Entonces, la pregunta es: cómo expresar ángulos de dos puntos en la elipse a dos pares de sus coordenadas. Una pregunta intermedia podría ser: Cómo encontrar el centro de una elipse por coordenadas de un par de puntos en su curva.

Actualizar : Tengamos una condición previa que indique que normalmente se coloca una elipse (sus radios son paralelos al eje del sistema de coordenadas lineales), por lo que no se aplica rotación.

Actualización : esta pregunta no está relacionada con el elemento svg: ellipse, sino con " a " comando de arco elíptico en svg: elemento de ruta ( Rutas SVG: los comandos de curva de arco elíptico )

¿Fue útil?

Solución

Entonces la solución está aquí:

La fórmula parametrizada de una elipse:

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

Pongamos coordenadas conocidas de dos puntos:

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

Ahora tenemos un sistema de ecuaciones con 4 variables: centro de elipse (x0 / y0) y dos ángulos t1, t2

Restemos las ecuaciones para eliminar las coordenadas del centro:

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

Esto puede reescribirse (con fórmulas de identidades de producto a suma) como:

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

Reemplacemos algunas de las ecuaciones:

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

Luego obtenemos un sistema de ecuaciones simples:

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

Dividir la primera ecuación por la segunda produce:

a1 = arctan(r1/r2)

Agregar este resultado a la primera ecuación da:

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

O, simple (usando composiciones de trigonométricas y funciones trigonométricas inversas):

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

o incluso más simple:

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

Ahora el sistema inicial de cuatro ecuaciones se puede resolver con facilidad y se pueden encontrar todos los ángulos y las coordenadas del centro del eclipse.

Otros consejos

El enlace de arco de curva elíptica que publicó incluye un enlace a notas de implementación de arco elíptico .

Allí, encontrará las ecuaciones para conversión del punto final a la parametrización central .

Aquí está mi implementación de JavaScript de esas ecuaciones, tomada de una demostración interactiva de rutas de arco elípticas , utilizando Sylvester.js para realizar los cálculos de matriz y vector .

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

Una elipse no se puede definir con solo dos puntos. Incluso un círculo (una elipse con carcasa especial) se define por tres puntos.

Incluso con tres puntos, tendrías elipses infinitas que pasan por estos tres puntos (piensa: rotación).

Tenga en cuenta que un cuadro delimitador sugiere un centro para la elipse, y probablemente asume que sus ejes mayor y menor son paralelos a los ejes x, y (o y, x).

La pregunta intermedia es bastante fácil ... tú no. Trabaja el centro de una elipse desde el cuadro delimitador (es decir, el centro del cuadro es el centro de la elipse, siempre que la elipse esté centrada en el cuadro).

Para su primera pregunta, miraría la forma polar de la ecuación de elipse, que está disponible en Wikipedia . Tendría que resolver también la excentricidad de la elipse.

O podría forzar los valores desde el cuadro delimitador ... determinar si un punto se encuentra en la elipse y coincide con el ángulo, e iterar a través de cada punto en el cuadro delimitador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top